运算符重载的概念如下:
-
运算符重载让类拦截常规的Python运算;
-
类可重载所有Python表达式运算符;
-
类也可重载打印、函数调用、属性点号运算等内置运算;
-
重载是类实例的行为想内置类型;
-
重载是通过提供特殊名称的类方法来实现的;
常见的运算符重载方法
方法 | 重载 | 调用 |
---|---|---|
__init__ | 构造函数 | 对象建立:X = Class(args) |
__del__ | 解析函数 | X对象收回 |
__add__ | 运算符+ | 如果没有__iadd__,X+Y,X+=Y |
__or__ | 运算符或 | 如果没有__ior__ |
__repr__,__str__ | 打印、转换 | print(X)、repr(X)、str(X) |
__call__ | 函数调用 | X(args, *kwargs) |
__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:k]=sequence |
__delitem__ | 索引和分片删除 | del X[key], del X[i:j] |
__len__ | 长度 | len(X),如果没有__bool__,真值测试 |
__bool__ | 布尔测试 | bool(X),真测试 |
__lt__,__gt__,__le__,__ge__,__eq__,__ne__ | 特定的比较 | XY…,x> |
__radd__ | 右侧加法 | Other + X |
__iadd__ | 增强的加法 | X += Y |
__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__,__delete__ | 描述符属性 | X.attr,X.attr=Value,del X.attr |
__new__ | 创建 | 在__init__之前创建对象 |
所有重载方法的名称前后都有两个下划线字符,以便把同类中定义的变量名区别开来。
构造函数和表达式:__init__和__sub__
-
>>> class Number:
-
... def __init__(self, start):
-
... self.data = start
-
... def __sub__(self, other):
-
... return Number(self.data - other)
-
...
-
>>> X = Number(5)
-
>>> Y = X - 2
-
>>> Y
-
<__main__.Number object at 0x10224d550>
-
>>> Y.data
-
3
索引和分片: __getitem__和__setitem__
基本索引
-
>>> class Index:
-
... def __getitem__(self, item):
-
... return item ** 2
-
...
-
>>>
-
>>> for i in range(5):
-
... I = Index()
-
... print(I[i], end=' ')
-
...
-
0 1 4 9 16
切片索引
-
>>> class Index:
-
... data = [5, 6, 7, 8, 9]
-
... def __getitem__(self, item):
-
... print('getitem: ', item)
-
... return self.data[item]
-
... def __setitem__(self, key, value):
-
... self.data[key] = value
-
...
-
>>> X = Index()
-
>>> print(X[1:4])
-
getitem: slice(1, 4, None)
-
[6, 7, 8]
-
>>> X[1:4] = (1, 1, 1)
-
>>> print(X[1:4])
-
getitem: slice(1, 4, None)
-
[1, 1, 1]
索引迭代:__getitem__
如果重载了这个方法,for循环每次循环时都会调用类的getitem方法;
-
>>> class stepper:
-
... def __getitem__(self, item):
-
... return self.data[item].upper()
-
...
-
>>>
-
>>> X = stepper()
-
>>> X.data = 'ansheng'
-
>>> for item in X:
-
... print(item)
-
...
-
A
-
N
-
S
-
H
-
E
-
N
-
G
迭代器对象:__iter__和__next__
-
>>> class Squares:
-
... def __init__(self, start, stop):
-
... self.value = start - 1
-
... self.stop = stop
-
... def __iter__(self):
-
... return self
-
... def __next__(self):
-
... if self.value == self.stop:
-
... raise StopIteration
-
... self.value += 1
-
... return self.value ** 2
-
...
-
>>> for i in Squares(1, 5):
-
... print(i)
-
...
-
1
-
4
-
9
-
16
-
25
成员关系:__contains__、__iter__和__getitem__
-
class Iters:
-
def __init__(self, value):
-
self.data = value
-
def __getitem__(self, item):
-
print('get[%s]' % item, end='')
-
return self.data[item]
-
def __iter__(self):
-
print('iter>==', end='')
-
self.ix = 0
-
return self
-
def __next__(self):
-
print('next:', end='')
-
if self.ix == len(self.data): raise StopIteration
-
item = self.data[self.ix]
-
self.ix += 1
-
return item
-
def __contains__(self, item):
-
print('contains: ', end=' ')
-
return item in self.data
-
X = Iters([1, 2, 3, 4, 5])
-
print(3 in X)
-
for i in X:
-
print(i, end='|')
-
print([i ** 2 for i in X])
-
print(list(map(bin, X)))
-
I = iter(X)
-
while True:
-
try:
-
print(next(I), end=' @')
-
except StopIteration as e:
-
break
属性引用:__getattr__和__setattr__
当通过未定义的属性名称和实例通过点号进行访问时,就会用属性名称作为字符串调用这个方法,但如果类使用了继承,并且在超类中可以找到这个属性,那么就不会触发。
-
>>> class empty:
-
... def __getattr__(self, item):
-
... if item == 'age':
-
... return 40
-
... else:
-
... raise AttributeError(item)
-
...
-
>>>
-
>>> x = empty()
-
>>> print(x.age)
-
40
-
>>> print(x.name)
-
Traceback (most recent call last):
-
File "<stdin>", line 1, in <module>
-
File "<stdin>", line 6, in __getattr__
-
AttributeError: name
-
>>> class accesscontrol:
-
... def __setattr__(self, key, value):
-
... if key == 'age':
-
... self.__dict__[key] = value
-
... else:
-
... raise AttributeError(key + ' not allowed')
-
...
-
>>>
-
>>> x = accesscontrol()
-
>>> x.age = 40
-
>>> print(x.age)
-
40
-
>>> x.name = 'Hello'
-
Traceback (most recent call last):
-
File "<stdin>", line 1, in <module>
-
File "<stdin>", line 6, in __setattr__
-
AttributeError: name not allowed
__repr__和__str__会返回字符串表达式
__repr__和__str__都是为了更友好的显示,具体来说,如果在终端下print(Class)则会调用__repr__,非终端下会调用__str__方法,且这两个方法只能返回字符串;
-
class adder:
-
def __init__(self, value=0):
-
self.data = value
-
def __add__(self, other):
-
self.data += other
-
def __repr__(self):
-
return 'addrepr(%s)' % self.data
-
def __str__(self):
-
return 'N: %s' % self.data
-
x = adder(2)
-
x + 1
-
print(x)
-
print((str(x), repr(x)))
右侧加法和原处加法: __radd__和__iadd__
只有当+右侧的对象是类实例,而左边对象不是类实例的时候,Python才会调用__radd__
-
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)
-
print(x + 1)
-
print('')
-
print(1 + y)
-
print('')
-
print(x + y)
使用__iadd__进行原处加法
-
class Number:
-
def __init__(self, val):
-
self.val = val
-
def __iadd__(self, other):
-
self.val += other
-
return self
-
x = Number(5)
-
x += 1
-
x += 1
-
print(x.val)
-
class Number:
-
def __init__(self, val):
-
self.val = val
-
def __add__(self, other):
-
return Number(self.val + other)
-
x = Number(5)
-
x += 1
-
x += 1
-
print(x.val)
Call表达式:__call__
当调用类实例时执行__call__方法
-
class Callee:
-
def __call__(self, *args, **kwargs):
-
print('Callee:', args, kwargs)
-
C = Callee()
-
C(1, 2, 3)
-
C(1, 2, 3, x=1, y=2, z=3)
-
class Prod:
-
def __init__(self, value):
-
self.value = value
-
def __call__(self, other):
-
return self.value * other
-
x = Prod(3)
-
print(x(3))
-
print(x(4))
比较:__lt__,__gt__和其他方法
类可以定义方法来捕获所有的6种比较运算符:<、>、<=、>=、==和!=
-
class C:
-
data = 'spam'
-
def __gt__(self, other):
-
return self.data > other
-
def __lt__(self, other):
-
return self.data < other
-
x = C()
-
print(x > 'han')
-
print(x < 'han')
布尔值测试:bool和len
-
class Truth:
-
def __bool__(self):
-
return True
-
X = Truth()
-
if X: print('yes')
-
class Truth:
-
def __bool__(self):
-
return False
-
X = Truth()
-
print(bool(X))
如果没有这个方法,Python退而求其次的求长度,因为一个非空对象看作是真:
-
>>> class Truth:
-
... def __len__(self): return 0
-
...
-
>>> X = Truth()
-
>>> if not X: print('no')
-
...
-
no
如果两个方法都有,__bool__会胜过__len__:
-
>>> class Truth:
-
... def __bool__(self): return True
-
... def __len__(self): return 0
-
...
-
>>> X = Truth()
-
>>> bool(X)
-
True
如果两个方法都没有定义,对象毫无疑义的看作为真:
-
>>> class Truth: pass
-
...
-
>>> bool(Truth)
-
True
对象解析函数:__del__
每当实例产生时,就会调用init构造函数,每当实例空间被收回时,它的对立面__del__,也就是解析函数,就会自动执行;
-
class Life:
-
def __init__(self, name='unknown'):
-
print('Hello, ', name)
-
self.name = name
-
def __del__(self):
-
print('Goodbye', self.name)
-
brian = Life('Brian')
-
brian = 'loretta'