面向对象的补充

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

1.1 tracemalloc

标准库tracemalloc,可以统计内存使用情况。

import tracemalloc


tracemalloc.start()
d = [dict(zip('xy', (4, 5)))for i in range(1000000)]  # 237M
t = [tuple(zip('xy', (4, 5)))for j in range(1000000)]  # 191M
snapshot = tracemalloc.take_snapshot()  # 快照
# stats = snapshot.statistics('lineno')
stats = snapshot.statistics('filename')
for s in stats:
    print(s)

1.2 slots

问题的引出:
字典为了提高查询效率,必须用空间换时间。一般来说一个实例,属性多一点,都存储在字典中便于查询,问题不大。但是如果数百万个实例,那么字典占的总空间就有点大了。
这个时候能不能把属性字典__dict__省了。python提供了__slots__方法。

class A:
    X = 1
    # __slots__ = 'x', 'y'
    # __slots__ = ('x', 'y')
    __slots__ = ['x', 'y']

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

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


a = A()
a.show()
print('A', A.__dict__)
print(a.__slots__)  # ('x', 'y')
# a.new = 10  # AttributeError
A.new = 10  # 可以动态的增加类色属性
print(A.__dict__)

  slots__告诉解释器,实例的属性都叫什么,一般来说,既然要节约内存,最好还是使用元组比较好。==一旦类提供了__slots,就阻止实例产生__dict__来保存实例的属性,也不能再动态增加实例的属性了。==
  尝试为实例a动态增加属性,a.new = 5,直接抛出AttributeError异常,说明实例的属性不能动态的增加了,A.new = 100,这是可以的,因为这是类的属性。
slots__不影响子类实例,不会继承下去,除非子类中也定义了__slots

1.2.1 应用场景

  使用需要构建在数百万以上众多对象,且内存容量较为紧张,实例的属性简单、固定且不用动态增加的场景。可以使用tracemalloc看看内存使用的差异。建议使用stats = snapshot(‘filename’)查看总内存使用。

import tracemalloc


tracemalloc.start()


class A:
    __slots__ = 'y', 'x'  # 阻止了实例字典的创建,无法阻止类属性的修改,没有继承
    # __slots__ = 'x y'.split()

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


print(A.__dict__)
# print(A().__dict__)
# print(A().x, A().y)
a = A()
# a.z = 100  # 会报错,__slots__阻止你对实例属性的修改
A.t = 2000
print(A.__dict__)
# a.y = 200
# print(a.y)  # AttributeError: y
d = [A() for i in range(1000000)]
s = tracemalloc.take_snapshot()
for x in s.statistics('filename'):
    print(x)  # 61.7M,有__slots__方法
    # 无__slots__方法时,占用空间为169M

1.3 未实现和未实现异常

print(type(NotImplementedError))  # <class 'type'> NotImplementedError为异常类
# <class 'type'>
print(type(None))  # <class 'NoneType'>
print(type(NotImplemented))  # <class 'NotImplementedType'>未实现单值
# <class 'NotImplementedType'>

NotImplemented是个值,单值,是NotImplementedType的实例。
NotImplementedError是类型,是异常类,返回type

1.4 运算符重载中的反向方法

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

    def __repr__(self):
        return "<A {}>".FORAMT(self.x)

    def __add__(self, other):
        print('add~~~~~~')
        if hasattr(other, 'x'):
            return self.x + other.x
        else:
            try:
                x = int(other)
            except:
                x = 0
            return self.x + x

    def __iadd__(self, other):
        print('iadd~~~~~~~')

        return A(self + other)

    def __radd__(self, other):
        print('radd~~~~~~~')

        return self + other  # 运算符重载,解释器看到+会调用__add__方法
#
#
# a1 = A(4)
# a2 = A(5)
# # print(a1 + a2)  # 9 int——> a1.__add__(a2)
# # print(a2 + a1)
# print(a1 + 1)  # 会报错 a1.__add__(1),而1没有x属性
# print(1 + a1)  # 会报错 1.__add__(a1)  int.__add__(1, a1)
# print(a1 + A(10))  # a1.__add__(A(10))


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


b1 = B(6)
print(a1 + b1)  # add~~~~~~ 10, a1.__add__(b1)
print(b1 + a1)  # radd~~~~~~~10, a1.__radd__(b1);b1没有add方法,或者有这个方法,但是返回值为NotImplemented,就会调用后面的反向方法

  b1 + a1等价于b1.add(a1),但是类B没有实现__add__方法,就去找a1的__radd__方法,1 + a1等价于1.add(a1),而int类型实现了__add__方法,不过这个方法对于这种加法的返回值是NitImplemented(未实现),解释器发现是这个值,就会发起对第二操作对象的__radd__方法的调用。

1.5 Python的对象模型

在python中,任何对象都有类型,可以使用type()或者__class__查看。
但是类型也是对象即类对象,它也有自己的类型。
对象模型

所有新类型的缺省类型都是type(可以使用元类来改变)
特殊类型type是所有对象的缺省类型,也包括type自己,但它又是一个对象,因此从object继承特殊类型object是继承树的顶层,它是python所有类型的最终基类。
也就是说,继承都来自object,类型都看type。type也是对象继承自object,object也有类型是type。这两又特殊,type类型是自己,object没有基类。

猜你喜欢

转载自blog.csdn.net/sqsltr/article/details/90640995
今日推荐