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'

属性引用:getattr__与__setattr

getattr

当对未定义(不存在)属性名称和实例进行点号运算时,就会用属性名称作为字符串调用这个方法;但如果Python可以通过其继承树搜索流程找到这个属性,该方法就不会被调用。

class empty:
    def __getattr__(self,attrname):
        if attrname == "age":
            return 40
        else:
            raise AttributeError,attrname
X = empty()
X.age
X.name

第一个语句结果为40,第二个会触发异常

此时的age变成了动态计算的属性

setattr

想使用该方法,要确定是通过对属性字典做索引运算来赋值任何实例属性,即是使用self.dict['name'] = x而不是self.name = x

__repr__和__str__返回字符串表达形式

打印操作首先尝试__str__和str内置函数,如果没有定义__str__,打印使用__repr__,不过在交互模式下只使用__repr__

为了确保一个定制显示能够在所有的环境中都显示而不管容器是什么,一般都是用__repr__

右侧加法__radd__

当+右侧的对象是类实例而左边对象不是类实例时,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表达式__call__

当调用实例时,使用__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

⚠️ **GitHub.com Fallback** ⚠️