魔术方法
魔术方法(Magic Methods 或 Dunder Methods,因为以双下划线 __ 包围)是 Python 对象模型的核心。它们让自定义类可以像内置类型一样工作——支持算术运算、比较、迭代、上下文管理等。
字符串表示
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self): # str() 和 print 使用
return "(%d, %d)" % (self.x, self.y)
def __repr__(self): # repr() 和交互式解释器使用
return "Point(%d, %d)" % (self.x, self.y)
p = Point(3, 4)
print str(p) # (3, 4)
print repr(p) # Point(3, 4)
print p # (3, 4),print 调用 __str__
__str__ 面向用户,应该友好可读。__repr__ 面向开发者,应该尽可能精确到可以 eval(repr(obj)) 重建对象。
算术运算符
class Vector(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # +
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other): # -
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar): # *
return Vector(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar): # 右乘:scalar * vector
return self * scalar
def __eq__(self, other): # ==
return self.x == other.x and self.y == other.y
def __str__(self):
return "Vector(%d, %d)" % (self.x, self.y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print v1 + v2 # Vector(4, 6)
print v1 * 3 # Vector(3, 6)
print 3 * v1 # Vector(3, 6),__rmul__
print v1 == Vector(1, 2) # True
容器协议
让对象支持 len()、索引、in 操作:
class Deck(object):
def __init__(self):
self._cards = []
def __len__(self): # len()
return len(self._cards)
def __getitem__(self, index): # obj[index]
return self._cards[index]
def __setitem__(self, index, value): # obj[index] = value
self._cards[index] = value
def __contains__(self, card): # in
return card in self._cards
def __iter__(self): # for x in obj
return iter(self._cards)
def add(self, card):
self._cards.append(card)
deck = Deck()
deck.add("Ace of Spades")
deck.add("King of Hearts")
print len(deck) # 2
print deck[0] # Ace of Spades
print "Ace" in deck # False(完整匹配)
for card in deck:
print card
实现了 __len__、__getitem__、__iter__ 后,对象可以像列表一样使用,还能自动支持 reversed()、list()、sorted() 等函数。
可调用对象
class Multiplier(object):
def __init__(self, factor):
self.factor = factor
def __call__(self, x): # obj(x)
return x * self.factor
double = Multiplier(2)
triple = Multiplier(3)
print double(5) # 10
print triple(5) # 15
实现了 __call__ 的对象可以像函数一样调用。这在创建参数化的函数工厂时很有用。
上下文管理器
class ManagedFile(object):
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self): # with 进入
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb): # with 退出
if self.file:
self.file.close()
return False # 不抑制异常
with ManagedFile("data.txt", "r") as f:
data = f.read()
常用魔术方法速查
| 方法 | 触发方式 | 说明 |
|---|---|---|
__init__ | Class() | 初始化方法 |
__str__ | str(obj), print | 用户友好的字符串 |
__repr__ | repr(obj) | 开发者字符串 |
__eq__ | == | 相等比较 |
__ne__ | != | 不等比较 |
__lt__ | < | 小于 |
__le__ | <= | 小于等于 |
__gt__ | > | 大于 |
__ge__ | >= | 大于等于 |
__add__ | + | 加法 |
__sub__ | - | 减法 |
__mul__ | * | 乘法 |
__len__ | len() | 长度 |
__getitem__ | obj[key] | 索引获取 |
__setitem__ | obj[key] = val | 索引设置 |
__delitem__ | del obj[key] | 索引删除 |
__contains__ | in | 成员检测 |
__iter__ | for x in obj | 迭代 |
__call__ | obj() | 调用 |
__enter__ | with 进入 | 上下文管理器 |
__exit__ | with 退出 | 上下文管理器 |
注意事项
- 魔术方法不是为"炫技"而存在的,只在确实需要模拟内置类型行为时使用
- 实现了比较方法后,考虑用
@functools.total_ordering装饰器自动生成剩余方法 - 算术运算符应返回新对象(不可变风格)或
self(可变风格),不要修改操作数