这本书上来就讲了魔法方法,也叫双下方法、特殊方法,通过两个例子对让读者了解了双下方法的用法,更重要的是,让我一窥Python的语言风格和给使用者的自由度。
第一个例子:一摞Python风格的纸牌:
import collections
Card = collections.namedtuple(‘ Card ‘ , [‘ rank ‘ , ‘ suit ‘ ])
class FrenchCard:
ranks = [str(n) for n in range(2, 11)] + list(‘ JQKA ‘ )
suits = ‘ clubs diamonds hearts spades ‘ .split()
def __init__ (self):
self._cards = [Card(rank, suit) for rank in self.ranks for suit in self.suits]
def __len__ (self):
return len(self._cards)
def __getitem__ (self, position):
return self._cards[position]
deck = FrenchCard()
suits_values = dict(clubs=0, diamonds=1, hearts=2, spades=3)
def high_spades(card):
ranks_value = FrenchCard.ranks.index(card.rank)
return ranks_value * len(suits_values) + suits_values[card.suit]
for card in sorted(deck, key=high_spades):
print (card)
这个例子让我了解到python的许多方法都是由双下方法构成了,比如在这个例子中,如果打印print(deck[0]),会显示Card(rank=‘2‘, suit=‘clubs’),这是因为类里面定义了__getitem__方法,如果没有这个方法的话,就会报错,而且,仅仅实现了__getitem__就可以迭代和反向迭代了。我看了下,list这个类里面也有__getitem__这个方法,那么,如果我们有需求的话,可以定制list里的__getitem__的方法(通过集成和派生)。不过,len这个方法有点特殊,我会在后面讲到哪里特殊。
通过__len__和__getitem__, FrenchDeck这个类,就和Python现有的序列数据类型一样,可以提现出Python的核心语言特性(例如迭代和切片)
另外,namedtuple是一个很好用的函数,用来创建一个只有少量属性,没有方法的类,比单独创建一个类方便多了,感觉和__slots__一点相似,但是没有在实战中用过,以后用到了再去深入了解吧!它的因为解释我觉得很清楚:Returns a new subclass of tuple with named field。
如何使用特殊方法
特殊方法是为了让Python解释器调用的,你自己并不需要调用它们。也就是说没有 my_object.__len__() 这种写法, 而应该使用 len(my_object)。在执行 len(my_object) 的时候,如果 my_object 是一个自定义类的对象,那么 Python 会自己去调用其中由 你实现的 __len__ 方法。除非使用元编程,否则你的代码通常无需使用特殊方法。
在处理python内置类型的时候,比如列表、字符串、字节等,CPython会走后门,__len__实际是直接返回PyVarObject里的ob_size属性,这个是通过C语言实现的,速度比调用这个方法快很多。
注意,只有是Python自带的数据结构才走这个后门,如果是自己定义的类里有__len__方法的话,是不会走这个后门的。这体现了Python之禅,实用胜于纯粹。len之所以不是一个普通方法(CPython直接从一个C结构体里读取对象的长度),原因是因为我们在编程的时候会大量使用Python自带的数据类型,而同时我们又可以把len用于自定义数据类型。这种处理方式在保持内置类型的效率和 保证语言的一致性之间找到了一个平衡点,也印证了“Python 之禅”中的 另外一句话:“不能让特例特殊到开始破坏既定规则。”
第二个例子:二维向量(vector)
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) # x * x + y * 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(1, 2)
v2 = Vector(2, 3)
print (v1) # Vector(1,2)
print (v1 + v2) # Vector(3,5)
v3 = Vector(0, 0)
print (bool(v3)) # False
这个例子使用了6个特殊方法,但是这6个特殊方法并不会在这个类自身的代码中使用,一般只有Python解释器会频繁地直接调用这些方法。
repr方法能把一个对象用字符串的形式表示,在老的使用 % 符号的字符串格式中,这个函数返回的结果用来代 替 %r 所代表的对象。当打印v1的时候会显示Vector(1,2),如果没有__repr__就会显示v1这个对象的内存地址。__repr__和__str__不同的是,使用交互器的时候只有__repr__方法会生效,使用print的时候会先找__str__,没有__str__在去找__repr__。
__add__和__mul__让Vector的对象可以在不改变操作对象的情况下产出一个新的值,实现加法和乘法的运算。
我们自己定义的__bool__的意思是如果一个向量的模是0(x,y都为0),就返回False。bool(v3)调用的就是v3.__bool__(),如果我们没有定义__bool__的话,那么bool(v3)就会去尝试调用v3.__len __(),如果返回0的话,就是False,否则就是True。
下面这种写法会让Vector.__bool__更高效,不过不如第一个好理解,却能省掉从 abs 到 __abs__ 到平方再到平方根这 些中间步骤。
def __bool__ (self):
return bool(self.x,self.y)
Ruby和Python都支持这些特殊方法,Ruby社区称之为魔法方法。Javascript在这方面无法是赶不上Python和Ruby的。
《流畅的python》读书笔记,第一章:python数据模型
原文:https://www.cnblogs.com/lshedward/p/10158852.html