7Python全栈之路系列之面向对象运算符重载
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了7Python全栈之路系列之面向对象运算符重载相关的知识,希望对你有一定的参考价值。
Python全栈之路系列之面向对象运算符重载
运算符重载的概念如下:
运算符重载让类拦截常规的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‘
以上是关于7Python全栈之路系列之面向对象运算符重载的主要内容,如果未能解决你的问题,请参考以下文章