• __slots__和运算符重载中的反向方法


    问题的引出

    都是字典惹的祸

    字典为了提升查询效率,必须用空间换时间

    一般来说一个多想,属性多一点,都存储在字典中便于查询,问题不大。

    但是如果数百万个对象,那么字典占的就有点大了。

    这个时候,能不能把属性字典__dict__省了?

    python提供了__slots__

    class A:
        x = 1
    
        def __init__(self):
            self.y = 5
            self.z = 6
    
        def show(self):
            print(self.x,self.y,self.z)
    
    
    a = A()
    print(A.__dict__)
    print(a.__dict__)
    
    结果为:
    {'__module__': '__main__', 'x': 1, '__init__': <function A.__init__ at 0x0000000001E670D8>, 'show': <function A.show at 0x0000000001E674C8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
    {'y': 5, 'z': 6}

    思考

    上面2个字典,谁的字典是个问题?

    实例多达百万个的时候,这么多存放实例属性的字典是个问题。

    class A:
        x = 1
        __slots__ = ("y")#元组
        #__slots__ = ["y","z"]#可以吗?
        # __slots__ = "y","z"#可以吗?
        # __slots__ = "y"
    
    
        def __init__(self):
            self.y = 5
            self.z = 6
    
        def show(self):
            print(self.x,self.y)
    
    
    a = A()
    a.show()
    print("A",A.__dict__)
    #print("OBJ",a.__dict__)
    print(a.__slots__)
    
    结果为:
    Traceback (most recent call last):
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 17, in <module>
        a = A()
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 11, in __init__
        self.z = 6
    AttributeError: 'A' object has no attribute 'z'

    上面报错,因为slots,只有一个槽位。直接就报错了。

    class A:
        x = 1
        __slots__ = ("y")#元组
    
    
    
        def __init__(self):
            self.y = 5
            #self.z = 6
    
        def show(self):
            print(self.x,self.y)
    
    
    a = A()
    a.show()
    print("A",A.__dict__)
    print(a.__dict__)
    
    结果为:
    1 5
    A {'__module__': '__main__', 'x': 1, '__slots__': 'y', '__init__': <function A.__init__ at 0x0000000001E560D8>, 'show': <function A.show at 0x0000000001E564C8>, 'y': <member 'y' of 'A' objects>, '__doc__': None}
    Traceback (most recent call last):
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 18, in <module>
        print(a.__dict__)
    AttributeError: 'A' object has no attribute '__dict__'

    上面又报错了。

    class A:
        x = 1
        __slots__ = ("y","z")#元组
        #__slots__ = ["y","z"]#可以吗?
        # __slots__ = "y","z"#可以吗?
        # __slots__ = "y"
    
    
        def __init__(self):
            self.y = 5
            #self.z = 6
    
        def show(self):
            print(self.x,self.y)
    
    
    a = A()
    a.show()
    print("A",A.__dict__)
    #print("OBJ",a.__dict__)
    print(a.__slots__)
    
    结果为:
    1 5
    A {'__module__': '__main__', 'x': 1, '__slots__': ('y', 'z'), '__init__': <function A.__init__ at 0x00000000026770D8>, 'show': <function A.show at 0x00000000026774C8>, 'y': <member 'y' of 'A' objects>, 'z': <member 'z' of 'A' objects>, '__doc__': None}
    ('y', 'z')

    __slots__告诉解释器,实例的属性都叫什么,一般来说,既然要节约内存,最好还是使用元组比较好。

    一旦类提供了__slots__,就阻止实例产生__dict__来保存实例的属性。

    尝试为实例a动态增加属性

    a.newx = 5

     说明实例不可以动态增加属性了

    A.NEWX = 20,这是可以的,因为这是类属性。

    继承

    class A:
        x = 1
        __slots__ = ("y","z")#元组
    
    
    
        def __init__(self):
            self.y = 5
            #self.z = 6
    
        def show(self):
            print(self.x,self.y)
    
    
    a = A()
    a.show()
    print("A",A.__dict__)
    print(a.__slots__)
    
    class B(A):
        def __init__(self):
            self.b1 = 500
    
    print("B",B().__dict__)

    结果为:

    1 5
    A {'__module__': '__main__', 'x': 1, '__slots__': ('y', 'z'), '__init__': <function A.__init__ at 0x0000000001E87E58>, 'show': <function A.show at 0x0000000001E87438>, 'y': <member 'y' of 'A' objects>, 'z': <member 'z' of 'A' objects>, '__doc__': None}
    ('y', 'z')
    B {'b1': 500}

    __slots__不影响子类实例,不会继承下去,查费子类里面自己也定义了__slots__。

    应用场景

    使用需要构建数百万以上对象,且内存容量较为紧张,实例的属性简单,固定且不用动态增加的场景。

    未实现和未实现异常

    print(type(NotImplemented))
    print(type(NotImplementedError))
    
    #<class 'NotImplementedType'>
    #<class 'type'>
    
    raise NotImplemented
    #raise NotImplementedError
    
    结果为;
    Traceback (most recent call last):
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 7, in <module>
        raise NotImplemented
    TypeError: exceptions must derive from BaseException
    <class 'NotImplementedType'>
    <class 'type'>
    NotImplemented是个值,单值,是NotImplementedType类的实例。
    NotImplementedError是类型,是异常,返回type。

    运算符重载中的反向方法

    前面学习过运算符重载的方法,例如add和iadd.

    class A:
        def __init__(self,x):
            self.x = x
    
        def __add__(self, other):#+
            print(self,"add")
            return self.x+other.x
    
        def __iadd__(self, other):#+=
            print(self,"iadd")
            return A(self.x + other.x)
    
        def __radd__(self, other):#+
            print(self,"radd")
            return self.x + other.x
    
    
    a = A(4)
    b = A(5)
    print(a,b)
    print(a+b)
    print(b+a)
    b+=a
    a+=b
    
    结果为:
    <__main__.A object at 0x00000000021FA248> <__main__.A object at 0x00000000021FA348>
    <__main__.A object at 0x00000000021FA248> add
    9
    <__main__.A object at 0x00000000021FA348> add
    9
    <__main__.A object at 0x00000000021FA348> iadd
    <__main__.A object at 0x00000000021FA248> iadd

    __radd__方法根本没有执行过?为什么?

    因为都是A的实例,都是调用的__add__,无非就是实例a还是实例b调用而已。

    测试一下a+1

    class A:
        def __init__(self,x):
            self.x = x
    
        def __add__(self, other):
            print(self,"add")
            return self.x+other.x
    
        def __iadd__(self, other):
            print(self,"iadd")
            return A(self.x + other.x)
    
        def __radd__(self, other):
            print(self,"radd")
            return self.x + other.x
    
    
    a = A(4)
    a+1
    
    结果为:
    <__main__.A object at 0x000000000222B248> add
    Traceback (most recent call last):
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 19, in <module>
        a+1
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 7, in __add__
        return self.x+other.x
    AttributeError: 'int' object has no attribute 'x'

    出现了AttributeError,因为1是int类型,没有x这个属性,还是__add__被执行了。

    测试1+a,运行结果如下:

    <__main__.A object at 0x00000000021EB208> radd
    Traceback (most recent call last):
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 19, in <module>
        1+a
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 15, in __radd__
        return self.x + other.x
    AttributeError: 'int' object has no attribute 'x'

    这次执行的是实例a的__radd__方法。

    1+a等价于1.__add__(a),而类型实现了__add__方法的,为什么却不抛出异常,而是执行了实例a的__radd__方法?

    再看一个例子

    class A:
        def __init__(self,x):
            self.x = x
    
        def __add__(self, other):
            print(self,"add")
            return self.x+other.x
    
        def __iadd__(self, other):
            print(self,"iadd")
            return A(self.x + other.x)
    
        def __radd__(self, other):
            print(self,"radd")
            return self.x + other.x
         #return self+other #相当于self.__add__(other)
    class B:#未实现__add__ def __init__(self,x): self.x = x a = A(4) b = B(10) print(a+b) print(b+a) 结果为: <__main__.A object at 0x00000000021EB408> add 14 <__main__.A object at 0x00000000021EB408> radd 14

    b+a等价于b.__add__(a),但是类B没有实现__add__方法,就去找a的__radd__方法。

    1+a等价于1.__add__(a),而int类型实现了__add__方法的,不过这个方法对于这种加法的返回值是notlmplemented,解释器发现是这个值,就会发起对第二操作对象的__radd__方法的调用。

    1+a能解决吗?

    class A:
        def __init__(self,x):
            self.x = x
    
        def __add__(self, other):
            print(self,"add")
            try:
                x = other.x
                return self.x + other.x
            except AttributeError:
                try:
                    x = int(other)
                except:
                    x = 0
                return self.x +x
    
    
        def __iadd__(self, other):
            print(self,"iadd")
            return A(self.x + other.x)
    
        def __radd__(self, other):
            print(self,"radd")
            return self.x + other.x
    
    class B:
        def __init__(self,x):
            self.x = x
    
    
    a = A(4)
    b = B(10)
    print(a+b)
    print(b+a)
    print(a+2)
    print(2+a)
    print(a+"abc")
    print("abc"+a)
    
    结果为:
    Traceback (most recent call last):
    <__main__.A object at 0x00000000021DA408> add
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 36, in <module>
        print(2+a)
    14
      File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 24, in __radd__
    <__main__.A object at 0x00000000021DA408> radd
        return self.x + other.x
    14
    AttributeError: 'int' object has no attribute 'x'
    <__main__.A object at 0x00000000021DA408> add
    6
    <__main__.A object at 0x00000000021DA408> radd

    “abc”+a,字符串也实现了__add__方法,不过默认是处理不了和其他类型的加法,就返回notlmplemented。

    class A:
        def __init__(self,x):
            self.x = x
    
        def __add__(self, other):
            print(self,"add")
            try:
                res = self.x + other.x
            except:
                try:
                    o = int(other)
                except:
                    o = 0
                res = self.x+o
            return res
    
    
        def __iadd__(self, other):
            print(self,"iadd")
            return A(self.x + other.x)
    
        def __radd__(self, other):
            print(self,"radd")
            return self.x + other.x
    
    class B:
        def __init__(self,x):
            self.x = x
    
    
    a = A(4)
    b = B(10)
    print(a+b)
    print(b+a)
    print(a+2)
    print(2+a)
     
  • 相关阅读:
    清除浮动的几种方法
    call() 、 apply() 、bind()方法的作用和区别!
    关于如何通过json更改背景图片
    js验证码实现
    解决python3 UnicodeDecodeError: 'gbk' codec can't decode byte
    Rest接口测试,巧用firebug插件
    PHP中字符串的连接和换行
    PHP内置函数file_put_content(),将数据写入文件,使用FILE_APPEND 参数进行内容追加
    PHP的三种输出方式
    PHP中的include、include_once、require、require_once
  • 原文地址:https://www.cnblogs.com/xpc51/p/12012506.html
Copyright © 2020-2023  润新知