魔术方法
python 中的魔术方法以 __
开头和结尾,例如:__len__
、__getitem__
等,详细的列表可以参考 Data model。
这些特殊方法能够让你自己的对象实现和支持以下的语言构架,并与之交互:
- 迭代
- 集合类
- 属性访问
- 运算符重载
- 函数和方法的的调用
- 对象的创建和销毁
- 字符串的表示形式和格式化
- 管理上下文(即 with 块)
隐式调用
特殊方法的存在是为了被 python 解释器隐式所调用,而不会被显示调用(除了 __init__
),也就是应该使用 len(my_object)
的方式,而不会是 my_object.__len__
的形式。
如果 my_object
是一个自定义对象,python 解释器也会直接调用实现的 __len__
方法。
如果my_object
是一个 python 内置对象,CPython
会直接抄近路,__len__
会直接返回 PyVarObject 里的 ob_size
属性,直接读取这个值比调用一个方法要快很多。PyVarObject 是表示内存长度可变的内置对象的 C 语言结构体。
for i in range x
这一句其实调用的是 iter(x)
函数,这个函数其实又是调用 x.__iter__()
方法。
repr()
函数可以把一个对象用字符串的形式表示出来,这个函数实际上调用的是 __repr__
方法。类似的 str()
函数调用的是 __str__()
方法。
+
调用 __add__()
方法,*
调用 __mul__()
方法。
bool(x)
调用的是 x.__bool__()
方法,如果 __bool__()
方法不存在,则会调用 __len__()
方法。
实现一个纸牌类
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import collections
Card = collections.namedtuple('Card',['rank','suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2,11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank,suit) for suit in self.suits for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self,position):
return self._cards[position]
if __name__ == "__main__":
deck = FrenchDeck()
# 计算纸牌数量
print(len(deck))
# 使用索引
print(deck[0])
print(deck[-1])
# 随机抽取
from random import choice
print(choice(deck))
print(Card('Q', 'hearts') in deck)
通过实现 __len__
和 __getitem__
两个特殊方法,使得自定义类 FrenchDeck
具有了和 python 内建序列类型数据一样的迭代和索引特性。
实现一个二维向量类
#!/usr/bin/python
# -*- coding: UTF-8 -*-
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)
if __name__ == "__main__":
v1 = Vector(2,4)
v2 = Vector(1,5)
print(v1 + v2)
v3 = Vector(3,4)
print(abs(v3))
v4 = v3 * 10
print(v4)