fluent python epub_《Fluent Python》- 01 Python数据模型

数据模型其实是对Python框架的描述,它规范了这门语言自身构建模块的接口,这些模块包括但不限于序列,迭代器,函数,类和上下文管理器

一摞Python风格的纸牌

主要说明两个方法   __getitem__  以及  __len__

Card = collections.namedtuple('Card', ['rank', 'suit'])#namedtuple,tuple的一种,不可变#名为Card,后面的rank,suit是其属性,简单来说就是一个不可变的对象包含['a', 'b']两个属性#可以通过 my_card = Card('rank', 'suit') 的方式简单的构造

classFrenchDeck:

ranks= [str(n) for n in range(2, 11)] + list('JQKA') #构造 '2 3 4...10 J Q K A'

suits = 'spades diamonds clubs hearts'.split()def __init__(self):

self._cards= [Card(rank, suit) for suit inself.suitsfor rank inself.ranks]def __len__(self):returnlen(self._cards)def __getitem__(self, item): #为了实现 obj[item] 这个操作

return self._cards[item]

首先我们构造出这个FrenchDeck类,主要是包含有两个方法 __getitem__  以及  __len__

当一个类实现了__getitem__方法时,便可以使用obj[index] 这种类似列表的方式去访问其元素,而 __len__ 方法是为了能让  len() 函数用作

简单来说,如果你在代码里用了obj[index] 会访问到  __getitem__ 方法,另一个同理

deck =FrenchDeck()print(len(deck)) #52

print(deck[0]) #Card(rank='2', suit='spades')

print(choice(deck)) #choice 从列表中随机访问 Card(rank='6', suit='diamonds')

符合预期值,其实一开始我们可能会有点不习惯,为什么要用len,而不是用  .length()  或者 .size() 这种方法来获取长度。Python采用这种方式其实也有其好处,假象一下,我们在Java中获取长度,有时可能不知道对方是采用了length() 方法还是size()方法,亦或者其他名称来获取长度,并没有统一的规范。

ranks列表的构造是采用了列表推导的方式构造了,这个下一节会说明(我刚看时看的也是很懵,不过通过结果倒推其实还是很容易理解的)

迭代通常是隐式的,譬如说一个集合类型没有实现__contains__方法,那么in运算符就会按顺序做一次迭代搜索。于是in运算符就可以用在我们的FrenchDeck类上:

print(Card('Q', 'hearts') in deck) #True

print(Card('Q', 'beasts') in deck) #False

那么怎么排序呢?我们就按照2,3,4.....K,A 的顺序,再加上花色,黑桃最大,红桃次之,方块再次之,梅花最小。

suit_values = dict(spades = 3, hearts = 2, diamonds = 1, clubs =0)defspades_high(card):

rank_value=FrenchDeck.ranks.index(card.rank)return rank_value * len(suit_values) +suit_values[card.suit]#这个用来排序的函数有些复杂#sorted的里的key可以是函数,也可以是lambda,我们可以把函数抽象成一个lambda#首先要明白这个函数是作用是什么,给定的参数是card换句话说,需要排序的列表里的参数得有card#然后通过这个card做些许操作,得到一个值,通过这个值来比较#FrenchDeck.ranks.index(card.rank) 这行其实是获取了FrenchDeck里的ranks,然后通过传入的card来判断,传入的card应该在哪个位置#举个例子,传入了Card(rank='J', suit='diamonds'),那么rank_value得到的答案就是'J'所在的下角标,也就是9#然后返回的是 9 * 4(因为有四种花色) + 这种花色的权值#这个排序的结果就是 (clubs, 2) ,(diamonds, 2) ... (spades, A)

for card in sorted(deck, key=spades_high):print(card)###结果#Card(rank='2', suit='clubs')#Card(rank='2', suit='diamonds')#Card(rank='2', suit='hearts')#Card(rank='2', suit='spades')#...#Card(rank='A', suit='diamonds')#Card(rank='A', suit='hearts')#Card(rank='A', suit='spades')

一些特殊方法

一些特殊方法是为了被Python解释器调用的,而自己并非需要调用这些。就像上面的那个长度,我们是通过len() 来获取,而非直接 .__len__()。

如果是Python的内置类型,比方说list,str等 CPython会抄近路,直接调用其ob_size属性,因为这个读取是比读函数快的。

很多时候这些特殊方法的调用是隐式的,比方说for i in x 这个语句,其实是用了iter(x) 方法,背后则是 x.__iter__()。这时你可能就会好奇了,我们的FrenchDeck其实并没有实现iter方法,却可以迭代,这是为什么。其实主要是__getitem__方法的功劳,关于迭代的具体流程之后还会说明。

还有不要想当然的去添加一些特殊方法,现在没有的,以后说不定会有

模拟数值类型

利用特殊方法,可以让自定义对象通过加号‘+’进行运算,简单来说,可以变相实现像C++那种重载运算符

classVector:def __init__(self, x = 0, y =0):

self.x=x

self.y=ydef __repr__(self): #类似于java里的 toString

return 'Vector(%r, %r)' %(self.x, self.y)def __abs__(self): #获取绝对值,可以通过abs() 来访问

return hypot(self.x, self.y) #通俗来说,就是获取以a, b为边的直角三角形斜边,放这里的意思就是向量长

def __bool__(self):returnbool(abs(self))def __add__(self, other): #可以用 + 来计算两个向量相加

x = self.x +other.x

y= self.y +other.yreturnVector(x, y)def __mul__(self, scalar): #可以和数字相乘

return Vector(self.x * scalar, self.y * scalar)

关于__repr__方法再多说两句,__repr__几乎等价于Java中的toString()函数(友情提示你一下,Java里你即使实现了toString方法,也不能用String强转)。

__str__ 函数的用途是在调用 str(obj)的时候访问的,但是print(obj) 是不会调用__str__ 方法的。不论你有没有实现__repr__

但是反过来说,如果你没有实现__str__ 函数,但是有__repr__函数,那么解释器会帮你调用__repr__方法。

特殊方法一览

和运算符无关的特殊方法

类别

方法名

字符串/字节序列表示形式

__repr__, __str__, __format__, __bytes__

数值转换

__abs__, __bool__, __complex__, __int__, __float__, __hash__, __index__

集合模拟

__len__, __getitem__, __setitem__, __delitem__, __contains__

迭代枚举

__iter__, __reversed__, __next__

可调用模拟

__call__

上下文管理

__enter__, __exit__

实例创建和销毁

__new__, __init__, __del__

属性管理

__getattr__, __setattr__, getattribute__, __setattribute__, __delattr__, __dir__

属性描述符

__get__, __set__, __delete__

跟类相关的服务

__prepare__, __instancecheck__, __subclasscheck__

和运算符相关的特殊方法

类别

方法名和对应的运算符

一元运算符

__neg__ - , __pos__ +, __abs__  abs()

众多比较运算符

__lt__  , __ge__  >=

算术运算符

__add__ +, __sub__ - , __mul__ *, __truediv__ /,  __floordiv //,  __mod__ %,  __divmod__  divmod(),  __pow__ **或pow(), __round__  round()

反向算术运算符

__radd__,  __rsub__,  __rmul__,  __rtruediv__,  __rfloordiv__, __rmod__,  __rdivmod__, __rpow__

增量赋值算术运算符

__iadd__,  __isub__,  __imul__ , __itruediv__, __ifloordiv__, __imod__, __ipow__

位运算符

__invert__  ~,  __lshift__ <>,  __and__ &, __or__ |, __xor__ ^

反向位运算符

__rlshift__,  __rrshift__, __rand__, __rxor__, __ror__

增量赋值位运算符

__ilshift__,  __irshift__, __iand__, __ixor__, __ior__

杂谈(非正式向)

这个表是真的难操控,姑且就这样吧

老实说,我最开始看这一节花了很多时间,这第一节确实有些硬核,用到了很多之前见都没见过的方法或者方式。

有的时候正着不行就反着来,从结果出发倒推代码逻辑,然后去理解里面的用意能帮助我们不少。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值