Python-面向对象魔术方法总结

tracemalloc

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

import tracemalloc

tracemalloc.start()
d = [dict(zip('xy', (5, 6))) for i in range(1000000)] #237MB
t = [tuple(zip('xy', (5, 6))) for i in range(1000000)] #191MB

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

for stat in top_stats:
    print(stat)

如下图所示,发现使用dict和tuple创建相同数量,使用的内存不一致,字段使用的内存比较多些 。

 

snapshot.statistics三种模式:

  • snapshot.statistics('filename')
  • snapshot.statistics('lineno')
  • snapshot.statistics('traceback')

 

 

从上面实现可以看出内存使用上字典还是比较占用空间的

__slots__

问题的引出

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

一般来说一个实例,属性多一点,都存储在字典中便于查询,问题不大 。但是如果数百万个实例,那么字典占用总空间就有点大。

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

Python提供了__slots__魔术方法   

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.__dict__)
# print(a.__dict__) #没有实例字典
#a.x = 100 #无法进行赋值
A.m = 100  #
print(A.m

#输出
1 5
{'__module__': '__main__', 'X': 1, '__slots__': ('y', 'z'), '__init__': <function A.__init__ at 0x00000221E1F05400>, 'show': <function A.show at 0x00000221E1F05488>, 'y': <member 'y' of 'A' objects>, 'z': <member 'z' of 'A' objects>, '__doc__': None}
{'__module__': '__main__', 'X': 1, '__slots__': ('y', 'z'), '__init__': <function A.__init__ at 0x00000221E1F05400>, 'show': <function A.show at 0x00000221E1F05488>, 'y': <member 'y' of 'A' objects>, 'z': <member 'z' of 'A' objects>, '__doc__': None, 'm': 100}
100

__slots__告诉解析器,实例的属性都叫什么,一般来说,既然要节约内存,最好还是使用元组比较好,如果创建后修改少则使用元组。  

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

尝试为实例增加属性,返回AttributeError,说明实例不可以动态增加属性了。 

A.m = 100是可以的,因为这个是类属性。  

 

 

 

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

 

应用场景   

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

可以使用tracemalloc查看内存使用差异,建议使用stats = snapshot.statistics('filename')查看总内存使用。 

 

未实现和未实现异常  

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

raise NotImplementedError('not......')

#属性

Traceback (most recent call last):
  File "D:/Python/test1.py", line 1304, in <module>
    raise NotImplementedError('not......')
NotImplementedError: not......
<class 'NotImplementedType'>
<class 'type'>

Notlmplemented是个值,单值,是NotlmplementedTyped的实例

NotlmplementedError是类型,是异常类,返回type  

 

运算符重载中的反向方法  

前面学习过运算符重载的方法,例如__add__和__iadd__、__radd__

 

 

 

可迭代对象

class A:
    def __init__(self):
        self.items =[100, 200, 300]

    def __iter__(self):
        # yield from self.items
        return iter(self.items) #返回一个迭代器

    def __reversed__(self):
        return reversed(self.items) #返回一个迭代器

a = A()

for x in a:
    print(x)

print('~' * 30)

for x in reversed(a):
    print(x)

#输出
100
200
300
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
300
200
100

 

class B:
    def __init__(self):
        self.count = 1

    def __iter__(self):
        print('iter~~~~~~~')
        return self

    def __next__(self): #迭代器,必须实现__iter__ 和__next__方法
        print('next~~~~~~~~')
        self.count += 1
        if self.count > 10:
            raise StopIteration
        return self.count
b = B()

for x in b:
    print(x)

print('~' * 30)
# print(next(b))
print(next(b, 'end~~~~~~'))

for循环in后面是一个可迭代对象,本质上调用了__iter__魔术方法拿到了一个迭代器,for循环驱动的是一个迭代器,相当于不断地调用一个迭代器的__next__方法 

 

实现了__iter__和__next__()方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常,示例如下:

 

 

类提供了__iter__魔术方法,返回一个迭代器,类的实例就是可迭代对象,确切地讲是正向迭代。

如果提供了__reversed__魔术方法,返回一个迭代器,就可以使用内建函数reversed来逆向迭代

 

生成器

Python中由生成器表达式或生成器函数生成,快速得到迭代器的方法,生成器一定是迭代器,内部实现了迭代器的接口。 

 

posted @ 2020-07-02 16:27  Alrenn  阅读(216)  评论(0)    收藏  举报