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

Posted xpc51

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了__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)

 

 

以上是关于__slots__和运算符重载中的反向方法的主要内容,如果未能解决你的问题,请参考以下文章

Python基础及语法

Python面向对象的运算符重载

如何正确重载 __add__ 方法?

运算符重载

Python中类方法重载---大部分

Python面向对象运算符重载