美文网首页
Python 魔法方法总结

Python 魔法方法总结

作者: dawsonenjoy | 来源:发表于2020-06-02 22:50 被阅读0次

    魔法方法

    魔法方法是python十分强大的一个特性,他允许我们自定义类的行为,从而实现各种功能,只要实现了对应的魔法方法,那么在进行指定的操作时,python解释器就会自动去调用这些方法,这些方法一般都是以双下划线__开头和结尾。
    (魔法方法也可以看做是一种python协议,其和类本身没有关系,他是python解释器可以直接调用的方法)

    使用注意

    魔法方法是针对于类对象的方法特性,python虚拟机在对一个实例对象进行指定操作时,会去寻找其对应的类对象上是否存在对应的魔法方法,从而调用该方法,举例:

    def call(self):
        print(self, "调用")
    
    class A:
        __call__ = call
    
    class B: pass
    
    class C: pass
    
    a = A()
    a()
    
    b = B()
    # 往类对象上添加__call__魔法方法
    B.__call__ = call
    # 调用成功
    b()
    
    c = C()
    # 往实例对象上添加
    c.__call__ = call
    # 这里会调用失败
    c()
    
    # <__main__.A object at 0x0000013F1E9AA588> 调用
    # <__main__.B object at 0x0000013F1EC347B8> 调用
    # TypeError: 'C' object is not callable
    

    基本魔法方法

    字符串展示相关
    __repr__(self)

    原始字符串展示,当直接展示对象时,会隐式调用该方法,行为:obj/repr(obj),举例:

    class A:
        def __init__(self, name):
            self.name = name
        def __repr__(self):
            return f"class: A, name: {self.name}"
    
    a = A("aaa")
    print(a)
    # class: A, name: aaa
    

    在交互式环境中(如idle、jupyter里,直接输入a,那么也是默认调用repr进行展示)

    __str__(self)

    字符串展示,例如调用printstr方法时都会首先去寻找该方法,如果没找到,则会寻找__repr__方法,行为:str(obj),举例:

    class A:
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return f"str - name:{self.name}"
    
    class B:
        def __init__(self, name):
            self.name = name
        def __repr__(self):
            return f"repr - name:{self.name}"
    
    print(A(1))
    print(B(2))
    # str - name:1
    # repr - name:2
    
    __repr__/__str__区别
    • 调用方法不同:两个分别有对应的调用方法-repr()/str(),而一些函数也会隐式地去调用对应的方法,举例:
    class A: pass
    
    a = A()
    print(a)
    # 等价于:print(str(a))/print(a.__str__())
    a
    # 等价于:a.__repr__()/repr(a)
    
    • 依赖关系不同:如果找不到__str__则会寻找__repr__,反之则不会
    • 展示目标不同:__repr__主要用于转成更底层的格式供开发人员使用,__str__主要用于简单的用户展示

    repr/str区别参考:
    https://blog.csdn.net/kongsuhongbaby/article/details/87398394
    https://blog.csdn.net/Watkins_OS/article/details/100042680

    __format__(self, formatstr)

    格式化对象时的行为,行为:format(obj, formatstr),举例:

    class A:
        def __init__(self):
            self.s = "A"
        def __format__(self, formatstr):
            return self.s * int(formatstr)
            # 根据传入的数字决定返回的字符串
    
    a = A()
    print(format(a, "10"))
    # 将a类转成字符串,并复制10次
    
    # AAAAAAAAAA
    
    集合/序列相关
    __len__(self)

    定义当被len()调用时的行为(返回容器中元素的个数),例如我们可以通过重写该方法实现一个自定义的list,其在计算长度时会将嵌套列表的长度也算进去,举例:

    class mlist(list):
        def __len__(self):
            mylen = 0
            for each in self:
                if isinstance(each, list):
                    mylen += len(mlist(each))
                else:
                    mylen += 1
            return mylen
            
    li = [1,2,3,[4,5,6,[7,8,9]]]
    mli = mlist(li)
    print(len(li), len(mli))
    # 4 9
    
    __getitem__(self, key)

    定义获取容器中指定元素的行为,行为:obj[key],举例:

    class A:
        def __getitem__(self, key):
            return "value:" + str(key)
    
    a = A()
    print(a[0])
    # value:0
    

    注:
    python内部为我们做了很多的优化,例如for循环的会先寻找该类是否实现了__iter____next__方法,如果没有,还回去寻找是否实现了__getitem__方法,如果有,则会传入数字尝试是否能够进行迭代,举例:

    class A:
        def __init__(self):
            self.data = [1, 2, 3]
        def __getitem__(self, item):
            return self.data[item]
    
    for each in A():
        print(each)
    # 没有实现__iter__方法也能进行迭代
    
    __setitem__(self, key, value)

    定义设置容器指定元素的行为,行为:obj[key] = value,举例:

    class A:
        def __init__(self):
            self.name = None
            self.age = None
        def __setitem__(self, key, value):
            self.name = key
            self.value = value
        def __str__(self):
            return "{}:{}".format(self.name, self.value)
    
    a = A()
    a["xxx"] = 100
    print(a)
    # xxx:100
    
    __delitem__(self, key)

    定义删除容器中指定元素的行为,行为:del obj[key],举例:

    class A:
        def __init__(self):
            self.data = [1, 2, 3]
        def __delitem__(self, key):
            self.data.pop(key)
        def __str__(self):
            return str(self.data)
    
    a = A()
    del a[1]
    print(a)
    # [1, 3]
    

    注:
    如果要定义不可变容器就只用定义__len____getitem__两个方法,如果要定义的是可变的还得定义__setitem____delitem__

    __contains__(self, key)

    判断是否存在,行为:key in obj,举例:

    class A:
        def __init__(self):
            self.data = [1, 2, 3]
        def __contains__(self, key):
            return key in self.data
    
    a = A()
    print(10 in a)
    
    __reversed__(self)

    定义被调用reversed方法时的行为,行为:reversed(obj),举例:

    class A(list):
        def __reversed__(self):
            print("取反操作")
            self.reverse()
            return self
    
    a = A([1,2, 3])
    print(reversed(a))
    # 取反操作
    # [2, 3, 1]
    
    __missing__(self, key)

    定义访问不存在的key时的行为,行为:obj[key]key不存在),举例:

    class A(dict):
        def __missing__(self, key):
            print(f"key:{key}不存在,默认返回0!")
            return 0
    
    
    a = A({"x": 1, "y": 2})
    print(a["z"])
    
    # key:z不存在,默认返回0!
    # 0
    
    迭代相关
    __iter__(self)

    返回一个迭代器,行为:iter(obj),而迭代器类必须要先实现__iter____next__这两个魔法方法,举例:

    class A:
        def __init__(self):
            self.data = [1, 2, 3]
            self.index = 0
        def __iter__(self):
            return self
        def __next__(self):
            self.index += 1
            return self.data[self.index - 1]
    
    a = A()
    ia = iter(a)
    print(next(a))
    print(next(a))
    print(next(a))
    print(next(a))
    
    __next__(self)

    决定了迭代器的迭代规则,行为:next(obj),举例实现斐波那契数列:

    class A:
        def __init__(self, n=10):
            self.a = 0
            self.b = 1
            self.n = n
        def __iter__(self):
            return self
        def __next__(self):
            self.a, self.b = self.b, self.a + self.b
            if self.a > self.n:
                raise StopIteration
            return self.a
    a = A()
    for each in a:
        print(each,end=' ')
    # 1 1 2 3 5 8 
    

    注:
    for循环实质就是将数据转成迭代器进行迭代输出,当接收到StopIteration异常时结束迭代,我们可以简单模拟for行为:

    class A:
        def __init__(self):
            self.data = [1, 2, 3]
            self.index = 0
        def __iter__(self):
            return self
        def __next__(self):
            self.index += 1
            if self.index > len(self.data):
                raise StopIteration
            return self.data[self.index - 1]
    
    def fake_for(data):
        """模拟for"""
        while True:
            i = iter(data)
            try:
                print(next(i))
            except StopIteration:
                break
    
    fake_for(A())
    
    调用相关
    __call__(self)

    将该对象当成函数调用时执行,行为:obj(),举例:

    class A:
        def __call__(self):
            print('1')
    
    a = A()
    a()
    # 1
    
    上下文管理器相关
    __enter__(self)

    实现了该__enter____exit__方法,就可以把当前类当做上下文管理器来使用(可以使用with关键字来进行处理),该方法定义了进入上下文时的操作,其中__enter__的返回值将会提供给with xxx as yyyyyy

    __exit__(self, exc_type, exc_value, traceback)

    定义了离开上下文时的操作,接收的几个参数是在上下文执行期间出现的异常,和上面的结合举例:

    class A:
        def __enter__(self):
            print("enter...")
            return self
        def do(self):
            print("context do")
        def __exit__(self, exc_type, exc_value, traceback):
            print("exit...")
    
    with A() as a:
        a.do()
        print("do something...")
    
    # enter...
    # context do
    # do something...
    # exit...
    

    当上下文中出现异常时,在__exit__中返回True则不会向外抛异常,否则向上一层抛出异常,举例:

    class A:
        def __enter__(self):
            print("enter...")
            return self
    
        def __exit__(self, exc_type, exc_value, traceback):
            print("exit...")
            # 如果不返回True,就会将异常向上抛出
            return True
    
    with A() as a:
        raise
    
    # enter...
    # exit...
    

    更多参考:https://blog.csdn.net/mydistance/article/details/86553431

    类型转换相关
    __bytes__(self)

    转字节值时操作,行为:bytes(obj),必须返回bytes类型,举例:

    class A:
        def __bytes__(self):
            return b"1"
    
    a = A()
    print(bytes(a))
    # b'1'
    
    __bool__(self)

    转布尔值时操作,行为:bool(obj)

    __int__(self)

    转整数值时操作,行为:int(obj)

    __float__(self)

    转浮点数时操作,行为:float(obj)

    __complex__(self)

    转复数时操作,行为:complex(obj)

    __hash__(self)

    转哈希值时操作,行为:hash(obj)

    __oct__(self)

    转八进制时操作,行为:oct(obj)

    __hex__(self)

    转十六进制时操作,行为:hex(obj)

    __index__(self)

    当一个类被用作索引/切片时调用,必须返回int类型,举例:

    class A:
        def __init__(self, x):
            self.x = x
        def __index__(self):
            return self.x
    
    a1 = A(1)
    a2 = A(3)
    li = ['a', 'b', 'c']
    print(li[a1])
    print(li[a1:a2])
    
    # b
    # ['b', 'c']
    

    可以看出A类被当做索引/切片时,会自动转成int类型,从而可以索引

    元类相关
    __new__(cls[, ...])

    实例化时第一个被调用的方法,接收的是需要进行实例化的类,其定义了实例化返回的内容,一般必须返回一个实例化对象,举例:

    class A:
        def __new__(cls):
            print(cls)
            return super().__new__(cls)
    
    a = A()
    print(a)
    # <class '__main__.A'>
    # <__main__.A object at 0x000001F27B7594A8>
    

    下面一种是错误的示范:

    class ABC:
        def __new__(cls, string):
            string = string.upper() #把字符串全变成大写
            return string
    
    a = ABC('abc')
    print(a)    #ABC
    

    此时实例化返回的是个str,虽然可以执行,但并不是正确的用法,比如上面这个返回对象被改成了str类型,所以a已经不是之前的类了,所以对应的__init__等方法也不会执行了,举例:

    class ABC:
        def __new__(cls,string):
            string = string.upper() #把字符串全变成大写
            return string
    
        def __init__(self, x):
            print(x)
    
    a = ABC('abc')
    

    有时候如果我们想在分配空间实例化之前执行一些代码的话也可以像下面这样重写:

    class My_dict(dict):
        def __new__(self, name):
            print("分配一个空间给自定义字典:", name)
            return super().__new__(self)
            # 因为是继承于dict类,所以使用dict类的__new__方法
            # 并且实例化的参数不需要在方法中传入,其会自动传入,只需传入对象本身即可
    
        def __init__(self, name):
            print("自定义字典:{},创建成功!".format(name))
    
    my_dict = My_dict("aaa")
    print(type(my_dict))
    
    __init__(self[, param1, param2, ...])

    初始化方法,实例化后初始化时被调用,接收的是实例化后的对象,举例:

    class ABC:
        def __init__(self,name):
            self.name = name
            print(name)
    
    abc = ABC('aaa')
    

    注:
    __init__方法返回的必须是None,否则无法成功初始化。因此像下面这种是不被允许的:

    class ABC:
        def __init__(self):
            return 0
    
    abc = ABC()
    #TypeError: __init__() should return None, not 'int'
    
    __del__(self)

    当对象被销毁时调用,举例:

    class A:
        def __init__(self, name):
            self.name = name
            print("create {}...".format(self.name))
        def __del__(self):
            print("delete {}...".format(self.name))
    
    a = A("a")
    del a
    print("...")
    

    注:
    该方法在对象被真正销毁的时候才会调用,并不是执行del obj就会调用,因为python内部的垃圾回收机制中使用了引用计数,而del语句只是将计数值减一,只有计数值为0的时候,才会调用__del__方法,举例:

    class A:
        def __init__(self, name):
            self.name = name
            print("create {}...".format(self.name))
        def __del__(self):
            print("delete {}...".format(self.name))
    
    a = A("a")
    b = a
    del a
    print("...")
    del b
    print("...")
    
    # create a...
    # ...
    # delete a...
    # ...
    

    可以看出a和b同时指向同一个对象,此时引用计数为2,当删除a以后,计数变成1,再删除b以后,计数变为0,才执行了对应的魔法方法

    属性相关
    __getattr__(self, name)

    当试图获取对象中不存在的属性时调用,行为:obj.attr/getattr(obj, name),举例:

    class A:
        def __init__(self, val):
            self.val = val
        def __getattr__(self, name):
            print("{}属性不存在".format(name))
    
    a = A("a")
    print(a.val)
    print(a.xxx)
    
    # a
    # xxx属性不存在
    # None
    

    使用场景:

    • 属性大小写通配,举例:
    class A:
        def __init__(self, name):
            self.name = name
        def __getattr__(self, item):
            return getattr(self, item.lower())
            # 当没找到属性名的时候,则将属性名转小写继续查找
    
    a = A("xxx")
    print(a.Name)
    
    # xxx
    
    • 返回内部维护的属性,举例:
    class A:
        _attr = {}
        def __init__(self, name):
            self._attr["name"] = name
        def __getattr__(self, item):
            return self._attr.get(item)
            # 当没找到属性名的时候,则进入内部维护的字典当中查找
    
    a = A("xxx")
    print(a.name)
    
    # xxx
    
    • 实现能够以属性方式获取字典内部值的字典,举例:
    class Mydict(dict):
        def __getattr__(self, name):
            return self[name]
    
    a = Mydict()
    a["x"] = 1
    print(a.x)
    
    # 1
    
    __setattr__(self, name, value)

    当一个属性被设置时调用,行为:obj.attr = val/setattr(obj, name, val),举例:

    class A:
        def __setattr__(self, name, value):
            print("设置属性:{}".format(name))
            super().__setattr__(name, value)
    
    a = A()
    a.x = 1
    print(a.x)
    
    # 设置属性:x
    # 1
    

    注:
    使用属性相关的魔法方法时要小心无限递归问题:例如__setattr__在设置属性时会被调用,假如自定义的__setattr__方法里也有设置属性值的操作时,可能就会无限递归调用自己,错误举例:

    class A:
        def __setattr__(self, name, value):
            self.x = value  # 在设置属性的魔法方法里设置属性
    a = A()
    a.x = 1     #此时就会无限循环报错
    
    • 解决方法1:
    class A:
        def __setattr__(self, name, value):
            super().__setattr__(name, value)    # 调用基类的__setattr__
            # object.__setattr__(self, name, value) # 两种本质都是一样的
    

    使用这种方式之所以不会造成无限递归是因为调用的是基类的__setattr__方法,因此当前类中的该方法并不会被触发,举例:

    class A:
        def __init__(self):
            # object.__setattr__(self, "data", 1)
            super().__setattr__("data", 1)
            # 上面两个都等价于:self.data = 1,并且不会触发到当前类的__setattr__方法
        def __setattr__(self, name, value):
            self.data = 2
    
    a = A()
    print(a.data)
    
    # 1
    

    可以看到并没有触发当前类的__setattr__方法(否则结果为2)

    • 解决方法2:
    class A:
        def __setattr__(self, name, value):
            super().__dict__[name] = value  #这句改成从类字典里(管理该类的属性)设置即可
    
    __delattr__(self, name)

    当一个属性被删除时调用,行为:del obj.attr/delattr(obj, name),举例:

    class A:
        def __init__(self):
            self.x = 1
        def __delattr__(self, name):
            print("删除属性:{}".format(name))
            super().__delattr__(name)
    
    a = A()
    print(a.x)
    delattr(a, "x")
    
    # 1
    # 删除属性:x
    
    __getattribute__(self, name)

    当该类的属性被访问时调用,不论属性存不存在都会调用,实际上在访问属性时都会先进入该方法,而该方法内部会对属性是否存在进行判断,如果不存在,就会在方法内部调用__getattr__,举例:

    class A:
        def __init__(self):
            self.x = 1
        def __getattribute__(self, name):
            print("访问属性:{}".format(name))
        def __getattr__(self, name):
            print("属性:{}不存在".format(name))
    
    a = A()
    print(a.x)
    print(a.y)
    
    # 访问属性:x
    # None
    # 访问属性:y
    # None
    

    可以看出这里我们访问不存在的属性y,却没有进入__getattr__方法,原因是我们重写了__getattribute__,导致默认的该方法被覆盖,我们可以在里面添加自己的逻辑,然后再调用默认的__getattribute__方法,举例:

    class A:
        def __init__(self):
            self.x = 1
        def __getattribute__(self, name):
            print("访问属性:{}".format(name))
            return object.__getattribute__(self, name)
        def __getattr__(self, name):
            print("属性:{}不存在".format(name))
    
    a = A()
    print(a.x)
    print(a.y)
    
    # 访问属性:x
    # 1
    # 访问属性:y
    # 属性:y不存在
    # None
    
    __dir__(self)

    获取类中所有属性调用,行为:dir(obj),举例:

    class A:
        def __init__(self):
            self.x = 1
        def __dir__(self):
            print("获取所有属性")
            return object.__dir__(self)
    
    a = A()
    print(dir(a))
    
    # 获取所有属性
    # ['__class__', ... '__weakref__', 'x']
    
    __sizeof__(self)

    对对象使用sys.getsizeof()时调用,行为:sys.getsizeof(obj),举例:

    import sys
    
    class A:
        def __init__(self):
            self.data = 1
        def __sizeof__(self):
            return self.data.__sizeof__()
    
    a = A()
    print(sys.getsizeof(a))
    
    # 52
    
    属性描述符相关
    __get__(self, instance, owner)

    实现该方法的类能够控制属性的访问,它返回属性的值,此时另一个类中的属性调用实现了该方法的类,则可以对属性进行控制举例:

    class Data:
        def __init__(self):
            self.x = 1
        def __get__(self, instance, owner):
            print(f"类:{owner.__name__}的对象-{instance}对{type(self).__name__}访问")
            return self.x
            # 访问A对象的data属性,返回的是Data对象的x属性
    
    class A:
        data = Data()
        # A对象的data属性有Data类来控制
    
    a = A()
    print(a.data)
    
    # 类:A的对象-<__main__.A object at 0x0000012801A9DCF8>对Data访问
    # 1
    
    __set__(self, instance, value)

    控制属性修改操作,不返回任何值

    __delete__(self, instance)

    控制属性删除操作,不返回任何内容,三个属性描述符结合举例:

    class Data:
        def __init__(self):
            self.x = 1
        def __get__(self, instance, owner):
            print(f"类:{owner.__name__}的对象-{instance}对{type(self).__name__}访问")
            return self.x
        def __set__(self, instance, value):
            if value >= 100:
                print("data必须小于100")
                return
            self.x = value
        def __delete__(self, instance):
            print("删除data")
    
    class A:
        data = Data()
        # data属性的get/set/delete由Data类来控制
    
    a = A()
    print(a.data)
    a.data = 10
    print(a.data)
    a.data = 1000
    del a.data
    
    # 类:A的对象-<__main__.A object at 0x00000220E6E0DCC0>对Data访问
    # 1
    # 类:A的对象-<__main__.A object at 0x00000220E6E0DCC0>对Data访问
    # 10
    # data必须小于100
    # 删除data
    

    实际上实现了这三个方法的类就算是property描述符了(属性描述符内部本身也是实现了这三个魔法方法的装饰器),将上面代码使用属性描述符实现举例:

    class A:
        def __init__(self):
            self.x = 1
        
        @property
        def data(self):
            return self.x
            # 用方法定义data,并将data当做属性使用,返回的是x的属性值
    
        @data.setter
        def data(self, val):
            if val >= 100:
                print("data必须小于100")
                return
            self.x = val
    
        @data.deleter
        def data(self):
            print("删除data")
    
    a = A()
    print(a.data)
    a.data = 10
    print(a.data)
    a.data = 1000
    del a.data
    
    # 1
    # 10
    # data必须小于100
    # 删除data
    
    反射相关
    __instancecheck__(self, instance)

    对当前对象使用isinstance(instance, class)时调用,行为:isinstance(xxx, obj),此时会判断某个对象是否为当前对象的实例,返回的值会自动被转成布尔类型,举例:

    class A:
        def __instancecheck__(self, instance):
            if isinstance(instance, (A, B)):
                return 1
            return 0
        
    class B: pass
    
    a = A()
    b = B()
    print(isinstance(a, a))
    print(isinstance(b, a))
    print(isinstance(1, a))
    # True
    # True
    # False
    
    __subclasscheck__(self, subclass)

    对当前对象使用issubclass(subclass, class)时调用,行为:issubclass(xxx, obj),此时会判断某个对象是否为当前对象的子类,返回的值会自动被转成布尔类型,举例:

    class A:
        def __subclasscheck__(self, subclass):
            if issubclass(subclass, (A, B)):
                return 1
            return 0
    
    class B: pass
    
    a = A()
    print(issubclass(A, a))
    print(issubclass(B, a))
    
    # True
    # True
    
    拷贝相关
    __copy__(self)

    对当前对象使用copy.copy()时调用,行为:copy.copy(obj),返回浅拷贝的对象,举例:

    from copy import copy
    
    class A:
        def __init__(self, data):
            self.data = data
        def __copy__(self):
            return type(self)(self.data[:])
    
    a = A([1,2,[3]])
    print(a.data)
    b = copy(a)
    b.data[2][0] = 100
    print(a.data)
    
    # [1, 2, [3]]
    # [1, 2, [100]]
    
    __deepcopy__(self, memodict={})

    对当前对象使用copy.deepcopy()时调用,行为:copy.deepcopy(obj),返回深拷贝的对象,举例:

    from copy import deepcopy
    
    class A:
        def __init__(self, data):
            self.data = data
        def __deepcopy__(self, memodict):
            return type(self)(deepcopy(self.data))
    
    a = A([1,2,[3]])
    print(a.data)
    b = deepcopy(a)
    b.data[2][0] = 100
    print(a.data)
    
    # [1, 2, [3]]
    # [1, 2, [3]]
    
    序列化相关
    __getstate__(self)

    将对象序列化成字节,行为:pickle.dump(file, obj)/pickle.dumps(obj),序列化默认是存储对象的__dict__属性,但我们可以自定义返回存储的内容,举例:

    import pickle
    class A:
        def __init__(self, x, y=0):
            self.x = x
            self.y = y
        def __getstate__(self):
            return {"x": self.x}
    
    a = A(5, 10)
    b = pickle.dumps(a)
    c = pickle.loads(b)
    print(c.__dict__)
    
    # {'x': 5}
    
    __setstate__(self, state)

    将字节反序列化成对象的行为,行为:pickle.load(file, obj)/pickle.loads(obj),举例:

    import pickle
    class A:
        def __init__(self, x, y=0):
            self.x = x
            self.y = y
        def __setstate__(self, state):
            self.data = state
    
    a = A(5, 10)
    b = pickle.dumps(a)
    c = pickle.loads(b)
    print(c.__dict__)
    
    # {'data': {'x': 5, 'y': 10}}
    
    __reduce__(self)

    序列化相关的更底层的接口方法,会返回对象的重构方法,以及对象对应的类、基类、__dict__属性等,如__getstate__底层就调用了该方法,举例:

    import pickle
    
    class A:
        x = 1
        def __init__(self):
            self.y = 2
        def __reduce__(self):
            print(111)
            return super().__reduce__()
    
    a = A()
    print(a.__reduce__())
    p = pickle.dumps(a)
    l = pickle.loads(p)
    print(l, l.__dict__)
    
    # 111
    # <__main__.A object at 0x00000212F76A9160> {'y': 2}
    # 111
    # (<function _reconstructor at 0x00000212F76D7158>, (<class '__main__.A'>, <class 'object'>, None), {'y': 2})
    
    __reduce_ex__(self, proto)

    相比于__reduce__,其可以传入协议版本,__reduce__相当于__reduce_ex__(0|1),该方法主要用于版本的兼容,当传入的proto值为0/12~时,执行的方式将有所不同,举例:

    class A:
        def __init__(self):
            self.y = 2
    
    a = A()
    print(a.__reduce__())
    print(a.__reduce_ex__(1))
    print(a.__reduce_ex__(2))
    
    # (<function _reconstructor at 0x00000222857E71E0>, (<class '__main__.A'>, <class 'object'>, None), {'y': 2})
    # (<function _reconstructor at 0x00000222857E71E0>, (<class '__main__.A'>, <class 'object'>, None), {'y': 2})
    # (<function __newobj__ at 0x00000222857E72F0>, (<class '__main__.A'>,), {'y': 2}, None, None)
    
    协程相关
    __await__(self)

    异步IO编程时,awaitable对象必须实现的方法,举例:

    import asyncio
    
    class A:
        def __await__(self):
            print("...")
            yield
    
    async def asyn_sleep():
        await asyncio.sleep(1)
        await  A()
    
    task = asyncio.wait([asyn_sleep() for _ in range(10)])
    loop = asyncio.get_event_loop()
    loop.run_until_complete(task)
    
    __aiter__(self)

    使用异步迭代器(async for)必须实现的方法之一,返回一个异步迭代器,示例将结合__anext__一起

    __anext__(self)

    使用异步迭代器必须实现的方法之一,实现异步迭代的规则,举例:

    import asyncio
    
    class A:
        def __init__(self):
            self.data = [1, 2, 3]
            self.index = 0
        def __aiter__(self):
            return self
        async def __anext__(self):
            self.index += 1
            if self.index >= len(self.data):
                raise StopAsyncIteration
            return self.data[self.index - 1]
    
    async def test():
        async for each in A():
            print(each)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(test())
    
    # 1
    # 2
    
    __aenter__(self)

    使用异步上下文(async with)必须实现的方法之一,在进入异步上下文时执行,示例将结合__aexit__一起

    __aexit__(self)

    使用异步上下文必须实现的方法之一,在离开异步上下文时执行,举例:

    import asyncio
    
    class A:
        async def __aenter__(self):
            print('enter context...')
            await asyncio.sleep(1)
            return self
     
        async def __aexit__(self, exc_type, exc, tb):
            print('exit context...')
            await asyncio.sleep(1)
    
    async def test():
        async with A() as a:
            print(a)
            await asyncio.sleep(1)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(test())
    
    # enter context...
    # <__main__.A object at 0x0000019AD4BDFD30>
    # exit context...
    

    更多异步相关内容参考:https://www.jb51.net/article/163540.htm

    数学运算相关魔法方法

    数学函数相关
    __abs__(self)

    计算绝对值时操作,行为:abs(obj),举例:

    class A:
        def __init__(self, val):
            self.val = val
        def __abs__(self):
            return abs(self.val)
    
    a = A(-100)
    print(abs(a))
    # 100
    
    __round__(self)

    四舍五入时操作,行为:round(obj)

    __floor__(self)

    向下取整时操作,行为:math.floor(obj)

    __ceil__(self)

    向上取整时操作,行为:math.ceil(obj)

    __trunc__(self)

    截取整数部分时操作,行为:math.trunc(obj)

    __divmod__(self, other)

    同时返回除数和余数操作,行为:divmod(obj, xxx),举例:

    class A:
        def __init__(self, x):
            self.x = x
        def __divmod__(self, other):
            return self.x // other.x, self.x % other.x
    
    a1 = A(5)
    a2 = A(2)
    print(divmod(a1, a2))
    
    # (2, 1)
    
    一元运算相关
    __neg__(self)

    前面加上负号时调用,行为:-obj,举例:

    class A:
        def __init__(self, x):
            self.x = x
        def __neg__(self):
            return -self.x
    
    a = A(1)
    print(-a)
    
    # -1
    
    __pos__(self)

    前面加上正号时调用,行为:+obj

    比较运算相关
    __lt__(self, other)

    使用<比较时调用,行为:xxx < obj,举例:

    class A:
        def __init__(self, x):
            self.x = x
        def __lt__(self, other):
            return self.x < other.x
    
    a1 = A(1)
    a2 = A(2)
    print(a1 < a2)
    
    # True
    
    __le__(self, other)

    使用<=比较时调用,行为:xxx <= obj

    __eq__(self, other)

    使用==比较时调用,行为:xxx == obj

    __ne__(self, other)

    使用!=比较时调用,行为:xxx != obj

    __gt__(self, other)

    使用>比较时调用,行为:xxx > obj

    __ge__(self, other)

    使用>=比较时调用,行为:xxx >= obj

    注:
    比较运算相关的魔法方法,只要实现了某一个,那么对应相反的操作也能同时被定义,例如定义了__lt__,那么当进行>操作时,就会自动调用__lt__取反的结果;同理>=<=都只要实现其中一个就行。而==!=,只需要前面四个中实现其中一个就能用。(但像只实现了>,那么>=/<=这些比较还是无法进行的),举例:

    class A:
        def __init__(self, x):
            self.x = x
        def __lt__(self, other):
            return self.x < other.x
    
    a1 = A(1)
    a2 = A(2)
    print(a1 < a2)  # True
    print(a1 > a2)  # False
    print(a1 == a2)  # False
    print(a1 >= a2) # 报错:没有实现对应的魔法方法
    
    算术运算相关
    __add__(self, other)

    当进行加法运算时调用,行为:obj + xxx,举例:

    class A:
        def __init__(self, x):
            self.x = x
        def __add__(self, other):
            return self.x + other.x
    
    a1 = A(1)
    a2 = A(2)
    print(a1 + a2)
    
    # 3
    
    __sub__(self, other)

    当进行减法运算时调用,行为:obj - xxx

    __mul__(self, other)

    当进行乘法运算时调用,行为:obj * xxx

    __truediv__(self, other)

    当进行真除法时调用,,行为:obj / xxx

    __floordiv__(self, other)

    当进行整除法时调用,行为:obj // xxx

    __mod__(self, other)

    当进行求余运算时调用,行为:obj % xxx

    __divmod__(self, other)

    同时计算除数和余数操作,行为:divmod(obj, xxx)

    __pow__(self, other)

    当进行幂运算时调用,行为:obj ** xxx

    位运算相关
    __invert__(self)

    取反运算,行为:~obj

    __lshift__(self, other)

    左移运算,行为:obj << xxx

    __rshift__(self, other)

    右移运算,行为:obj >> xxx

    __xor__(self, other)

    异或运算,行为:obj ^ xxx

    __and__(self, other)

    与运算,行为:obj & xxx

    __or__(self, other)

    或运算,行为:obj | xxx

    反向算术运算相关

    反向和普通的运算的差别:前面的普通运算,例如加法运算行为:obj + xxx,而反向加法运算就行为:xxx + obj

    __radd__(self, other)

    反向加法,行为:xxx + obj,举例:

    class A:
        def __init__(self, x):
            self.x = x
        def __radd__(self, other):
            return self.x + other
    
    a = A(1)
    print(1 + a)
    # 如果计算:a + 1,则不会调用到__radd__方法
    
    # 2
    
    __rsub__(self, other)

    反向减法,行为:xxx - obj

    __rmul__(self, other)

    反向乘法,行为:xxx * obj

    __rtruediv__(self, other)

    反向真除法,行为:xxx / obj

    __rfloordiv__(self, other)

    反向整除法,行为:xxx // obj

    __rmod__(self, other)

    反向求余,行为:xxx % obj

    __rdivmod__(self, other)

    反向计算除数和余数操作,行为:divmod(xxx, obj)

    __rpow__(self, other)

    反向幂运算,行为:xxx ** obj

    __rlshift__(self, other)

    反向左移运算,行为:obj << xxx

    __rrshift__(self, other)

    反向右移运算,行为:obj >> xxx

    __rxor__(self, other)

    反向异或运算,行为:obj ^ xxx

    __rand__(self, other)

    反向与运算,行为:obj and xxx

    __ror__(self, other)

    反向或运算,行为:obj or xxx

    增量算术运算相关
    __iadd__(self, other)

    增量赋值加法运算,行为:obj += xxx,举例:

    class A:
        def __init__(self, v):
            self.a = []
            self.a.extend(v)
        def __iadd__(self, v):
            self.a.extend(v)
            return A(self.a)
        def __repr__(self):
            return str(self.a)
    
    a = A([1,2,3])
    a += [4,5,6]
    print(a)
    # [1, 2, 3, 4, 5, 6]
    
    __isub__(self, other)

    行为:obj -= xxx

    __imul__(self, other)

    行为:obj *= xxx

    __itruediv__(self, other)

    行为:obj /= xxx

    __ifloordiv__(self, other)

    行为:obj //= xxx

    __imod__(self, other)

    行为:obj %= xxx

    __ipow__(self, other)

    行为:obj **= xxx

    __ilshift__(self, other)

    行为:obj <<= xxx

    __irshift__(self, other)

    行为:obj >>= xxx

    __ixor__(self, other)

    行为:obj ^= xxx

    __iand__(self, other)

    行为:obj &= xxx

    __ior__(self, other)

    行为:obj |= xxx

    更多魔法方法参考

    http://bbs.fishc.com/thread-48793-1-1.html
    https://blog.csdn.net/bluehawksky/article/details/79027055
    https://blog.csdn.net/yusuiyu/article/details/88292460
    https://www.jianshu.com/p/81e9be7be993

    相关文章

      网友评论

          本文标题:Python 魔法方法总结

          本文链接:https://www.haomeiwen.com/subject/wgpxzhtx.html