Python1005-基础知识补充

__slots__

问题的引出

都是字典惹的祸。
字典为了提升查询效率,必须用空间换时间。
一般来说一个对象,属性多一点,都存储在字典中便于查询,问题不大。
但是如果数百万个对象,那么字典占的就有点大了。
这个时候,能不能把属性字典__dict__省了?
Python提供了__slots__

class A:
    X = 1
    def __init__(self):
        self.x = 5
        self.y = 6

    def show(self):
        print(self.X, self.y, self,z)


a = A()
print(1, A.__dict__)
print(2, a.__dict__)
~~~~~~~~~~~~~~~~~~~~~~~~~~
1 {'__init__': <function A.__init__ at 0x000001F2327E19D8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'X': 1, 'show': <function A.show at 0x000001F2327E1A60>, '__module__': '__main__'}
2 {'x': 5, 'y': 6}

思考:
上面2个字典,谁的字典是个问题?
实例多大百万个的时候,这么多存放实例属性的字典是个问题

class A:
    X = 1
    __slots__ = ('y', 'z')    # 元组
    # __slots__ = ['y', 'z']    # 列表,可以吗
    # __slots__ = 'y', 'z'
    # __slots__ = 'y'           # AttributeError: 'A' object has no attribute 'z'


    def __init__(self):
        self.y = 5
        self.z = 6

    def show(self):
        print(self.X, self.y)


a = A()
a.show()

print(1, 'A', A.__dict__)
# print(2, 'obj', a.__dict__)
print(3, a.__slots__)

__slots__告诉解释器,实例的属性都叫什么,一般来说,既然要节约内存,最好还是用元组比较好。
一旦类提供了slots,就阻止实例产生__dict__来保存实例的属性。

尝试为实例a动态增加属性
a.newx = 5
返回AttributeError: ‘A’ object has no attribute ‘newx’
说明实例不可以动态增加属性了
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(1, 'A', A.__dict__)
# print(2, 'obj', a.__dict__)
print(3, a.__slots__)


class B(A):
    pass

print(4, 'B', B().__dict__)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 5
1 A {'y': <member 'y' of 'A' objects>, 'z': <member 'z' of 'A' objects>, '__slots__': ('y', 'z'), '__init__': <function A.__init__ at 0x000001FC56B719D8>, 'X': 1, '__doc__': None, '__module__': '__main__', 'show': <function A.show at 0x000001FC56B71A60>}
3 ('y', 'z')
4 B {}

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

应用场景

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

未实现和未实现异常

print(type(NotImplemented))
print(type(NotImplementedError))

# raise NotImplemented  # TypeError: exceptions must derive from BaseException
raise NotImplementedError
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<class 'NotImplementedType'>
<class 'type'>


Traceback (most recent call last):
  File "C:/python10/code/Python10/test.py", line 5, in <module>
    raise NotImplementedError
NotImplementedError

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(1, a, b)
print(2, a + b)
print(3, b + a)
b += a
a += b
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 <__main__.A object at 0x0000029198F8A278> <__main__.A object at 0x0000029198F8A3C8>
<__main__.A object at 0x0000029198F8A278> add
2 9
<__main__.A object at 0x0000029198F8A3C8> add
3 9
<__main__.A object at 0x0000029198F8A3C8> iadd
<__main__.A object at 0x0000029198F8A278> 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Traceback (most recent call last):
<__main__.A object at 0x0000029688482320> add
  File "C:/python10/code/Python10/test.py", line 19, in <module>
    a + 1
  File "C:/python10/code/Python10/test.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 0x00000210BE3C2320> radd
Traceback (most recent call last):
  File "C:/python10/code/Python10/test.py", line 19, in <module>
    1 + a
  File "C:/python10/code/Python10/test.py", line 15, in __radd__
    return self.x + other.x
AttributeError: 'int' object has no attribute 'x'

这次执行的是实例a的__radd__方法。
1 + a等价于1.__add__(a),而int类型实现了__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


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 0x0000021138F4A278> add
14
<__main__.A object at 0x0000021138F4A278> radd
14

b + a等价于b.__add__(a),但是类B没有实现__add__方法,就去找a的__radd__方法
1 + a等价于1.__add__(a),而int类型实现了__add__方法的,不过这个方法对于这种加法的返回值是NotImplemented,解释器发现是这个值,就会发起对第二操作对象__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 + other


class B:
    def __init__(self, x):
        self.x = x

a = A(4)
b = B(10)
print(1, a + b)
print(2, b + a)
print(3, a + 2)
print(4, 2 + a)
print(5, a + 'abc')
print(6, 'abc' + a)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<__main__.A object at 0x000001E3019EA7F0> add
1 14
<__main__.A object at 0x000001E3019EA7F0> radd
<__main__.A object at 0x000001E3019EA7F0> add
2 14
<__main__.A object at 0x000001E3019EA7F0> add
3 6
<__main__.A object at 0x000001E3019EA7F0> radd
<__main__.A object at 0x000001E3019EA7F0> add
4 6
<__main__.A object at 0x000001E3019EA7F0> add
5 4
<__main__.A object at 0x000001E3019EA7F0> radd
<__main__.A object at 0x000001E3019EA7F0> add
6 4

‘abc’ + a, 字符串也实现了__add__方法,不过默认是处理不了和其他类型的加法,就返回NotImplemented.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值