Python运算符重载 - littleboy12580/learning_python GitHub Wiki
运算符重载只是意味着在类方法中拦截内置的操作,当类的实例出现在内置操作中,Python自动调用重载的方法,方法的返回值变成了相应操作的结果。
- 运算符重载让类拦截常规的Python运算
- 类可重载所有Python表达式运算符
- 类也可重载打印,函数调用,属性点号运算等内置运算
- 重载使类实例的行为像内置类型
- 重载是通过提供特殊名称的类方法来实现的
方法 | 重载 | 调用 |
__init__ | 构造函数 | 对象建立:X = Class(args) |
__del__ | 析构函数 | X对象收回 |
__add__ | 运算符+ | 如果没有_iadd_,X+Y,X+=Y |
__or__ | 运算符|(位OR) | 如果没有_ior_,X|Y,X|=Y |
__repr__,__str__ | 打印、转换 | print(X)、repr(X),str(X) |
__call__ | 函数调用 | X(*args,**kargs) |
__getattr__ | 点号运算 | X.undefined |
__setattr__ | 属性赋值语句 | X.any = value |
__delattr__ | 属性删除 | del X.any |
__getattribute__ | 属性获取 | X.any |
__getitem__ | 索引运算 | X[key],X[i:j],没__iter__时的for循环和其他迭代器 |
__setitem__ | 索引赋值语句 | X[key] = value,X[i:j] = sequence |
__delitem__ | 索引和分片删除 | del X[key],del X[i:j] |
__len__ | 长度 | len(X),如果没有__bool__,真值测试 |
__bool__ | 布尔测试 | bool(X),真测试 |
__lt__,__gt__, | 特定的比较 | X < Y,X > Y |
__le__,__ge__, | X<=Y,X >= Y | |
__eq__,__ne__ | X == Y,X != Y | |
__radd__ | 右侧加法 | Other+X |
__iadd__ | 实地(增强的)加法 | X += Y (or else __add__) |
__iter__,__next__ | 迭代环境 | I = iter(X),next(I) |
__contains__ | 成员关系测试 | item in X (任何可迭代的) |
__index__ | 整数值 | hex(X),bin(X),oct(X),O[X],O[X:] |
__enter__,__exit__ | 环境管理器 | with obj as var: |
__get__,__set__ | 描述符属性 | X.attr,X.attr = value,del X.attr |
__new__ | 创建 | 在__init__之前创建对象 |
分片的重载是__getitem__;而索引的重载是__index__,针对一个实例返回一个整数值
class Indexer:
data = [5,6,7,8,9]
def __getitem__(self.index):
print('getitem:',index)
return self.data[index]
X = Indexer()
X[0]
X[2:4]
最终结果为
getitem:0
5
getitem:slice(2,4,None)
[7,8]
class C:
def __index__(self):
return 255
X = C()
oct(X)
('C' * 256)[X]
最终结果为
'0o377'
'C'
当对未定义(不存在)属性名称和实例进行点号运算时,就会用属性名称作为字符串调用这个方法;但如果Python可以通过其继承树搜索流程找到这个属性,该方法就不会被调用。
class empty:
def __getattr__(self,attrname):
if attrname == "age":
return 40
else:
raise AttributeError,attrname
X = empty()
X.age
X.name
第一个语句结果为40,第二个会触发异常
此时的age变成了动态计算的属性
想使用该方法,要确定是通过对属性字典做索引运算来赋值任何实例属性,即是使用self.dict['name'] = x而不是self.name = x
打印操作首先尝试__str__和str内置函数,如果没有定义__str__,打印使用__repr__,不过在交互模式下只使用__repr__
为了确保一个定制显示能够在所有的环境中都显示而不管容器是什么,一般都是用__repr__
当+右侧的对象是类实例而左边对象不是类实例时,Python会调用__radd__方法,其余情况则由左侧对象调用__add__方法。
class Commuter:
def __init__(self,val):
self.val = val
def __add__(self,other):
print('add',self.val,other)
return self.val + other
def __radd__(self,other):
print('radd',self.val,other)
return other + self.val
x = Commuter(88)
y = Commuter(99)
x + 1
1 + y
运行结果为
add 88 1
89
radd 99 1
100
当调用实例时,使用__call__方法,该方法支持各种参数传递方式,传递给实例的任何内容都会传递给该方法,包括通常隐式的实例参数,例如
class C:
def __call__(self,a,b,c=5,d=6):pass
class C:
def __call__(self,*args,**kwargs):pass
class C:
def __call__(self,*args,**kwargs):pass
都能匹配如下所有的实例调用
X = C()
X(1,2)
X(1,2,3,4)
X(a=1,b=2,d=4)
X(*[1,2],**dict(c=3,d=4))
X(1,*(2,),c=3,**dict(d=4))
具体例子如下:
class Prod:
def __init__(self,value):
self.value = value
def __call__(self,other):
return self.value * other
x = Prod(2)
x(3)
x(4)
运行结果为6和8