《流畅的python》第一章 Python数据模型

xiaoxiao2021-02-28  124

这一章主要讲述如何用特殊方法去激活一些基本的对象操作,这些特殊方法的名字以两个下划线开头,以两个下划线结尾(例如getitem)比如obj[key]的背后就是getitem方法,为了能求得my_collection[key]的值,解释器实际上会调用my_collection.getitem(key)。 本章的实例是一摞python风格的纸牌,很有嚼头。 1.1纸牌实例

第一个示例1-1 建立了一个纸牌类:

import collections #【1】 Card = collections.namedtuple('Card', ['rank', 'suit']) class FrenchDeck:#【2】 ranks = [str(n) for n in range(2, 11)] + list('JQKA') suits = 'spades diamonds clubs hearts'.split() def __init__(self):#【3】 self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks] def __len__(self):#【4】 return len(self._cards) def __getitem__(self, position):#【5】 return self._cards[position]

【1】collections模块,Python拥有一些内置的数据类型,比如str, int, list, tuple, dict等, collections模块在这些内置数据类型的基础上,提供了几个额外的数据类型: 1.namedtuple(): 生成可以使用名字来访问元素内容的tuple子类 2.deque: 双端队列,可以快速的从另外一侧追加和推出对象 3.Counter: 计数器,主要用来计数 4.OrderedDict: 有序字典 5.defaultdict: 带有默认值的字典 个人理解,这个namedtuple就有点像一个临时表,例子中的Card是表名,rank和suit是字段名。 【2】一个短小精悍的类,这个对ranks和suits数据集进行了赋值,同时重构了init/len/getitem这三个方法。 【3】用self._cards创建一副完整的扑克牌实例 【4】计算这幅扑克牌的长度 【5】获取每张扑克牌的位置


想一想,如果自己写一副类似扑克牌,要多少代码,而高手的代码就是这么简洁高效。 用上面的代码,可以轻松的得到一个纸牌对象,比如:

deck=FrenchDeck() print(len(deck))

从一叠牌里面抽取特定的一张也很简单:deck[0]或者deck[-1],这里面0是正向数第一张扑克牌,-1是逆向数第一张。 如果想随机抽取一张纸牌也很简单,用random.choice,代码如下:

from random import choice choice(deck)

可以看到,每次抽取的纸牌都不一样,这就是造好的随机数轮子(我们不需要自己去生成随机数然后再挑选相应的对象,一个函数搞定),很方便。 因为getitem方法把[]操作交给了self._cards列表,所以我们的deck类自动支持切片,例如查看一摞牌最上面3张和只看牌面是A的牌的操作:

deck[:3] deck[12::13]#【6】

【6】这里12的意思是索引号为12的牌A,后面的13表示每隔13张抽一次。要把正副扑克牌显示出来也很easy,可迭代的deck用一个for循环就可以便利:

for card in deck: print(card)

反向迭代同样很简单:

for card in reversed(deck): print(card)

接下来做一个稍微复杂的操作,排序,就是用点数来判定扑克牌的大小,2最小,A最大;同时黑桃最大,红桃次之,方块再次,梅花最小。

suit_values=dict(spades=3,hearts=2,diamonds=1,clubs=0) def spades_high(card): rank_value=FrenchDeck.ranks.index(card.rank)#【7】 return rank_value * len(suit_values)+suit_values[card.suit]

下面调用spades_high函数对这摞牌进行升序排序

for card in sorted(deck,key=spades_high): print(card)

【7】这段代码是不是有点难懂,慢慢研究一下。 rank_value的值等于FrenchDeck类下面ranks列表的索引值index(card.rank),card是一个Card类的对象,具有rank和suit两种属性。

1.2如何使用特殊方法 特殊方法的存在是为了被Python解释器调用的,程序员无需直接调用这些方法,也就是说没有my_object.len()这种写法,而应该使用len(my_object)。 本部分举了一个二维向量的例子,下面是定义的一个Vector类,这个类重构了repr/abs/bool/add/mul这些特殊方法

from math import hypot class Vector: def __init__(self, x=0, y=0): self.x = x self.y = y def __repr__(self): return 'Vector(%r, %r)' % (self.x, self.y) def __abs__(self): return hypot(self.x, self.y) def __bool__(self): return bool(abs(self)) def __add__(self, other): x = self.x + other.x y = self.y + other.y return Vector(x, y) def __mul__(self, scalar): return Vector(self.x * scalar, self.y * scalar)

有了这个类,就可以实现向量加法了:

v1=Vector(2,4) v2=Vector(2,1) print(v1+v2)

调用abs函数也没有问题:

v=Vector(3,4) print(abs(v))

需要注意的是%r和%s的区别 %r用rper()方法处理对象 %s用str()方法处理对象 有些情况下,两者处理的结果是一样的,比如说处理int型对象。 本书的作者认为,%和str.format这两种格式化字符串的手段在本书中都会使用,但作者偏向于str.format,python程序员更喜欢%,这两种形式并存的情况还会持续下去。

转载请注明原文地址: https://www.6miu.com/read-60195.html

最新回复(0)