zoukankan      html  css  js  c++  java
  • 类的__slots__属性

    为什么有"slots"属性?

    默认情况下,python对象队象的每个实例(instance)都会有一个字典来存储该实例的属性,这样做的好处在于运行时期每个对象可以任意设置新的属性。而相对应的坏处是,当创建成百上千个这样的实例的时候回很浪费内存。所以引入__slots__,用来指定实例只拥有固定的属性,因此python会给每个实例对象分配固定的内存空间,从而减少内存消耗。而且使用__slots__可以加快属性的访问。

    用法

    __slots__可以被设置成属性名称的字符串,可遍历的对象或者序列。
    之前在看odoo源码缓存相关的内容时,看到过下面这个例子:

    class ormcache_counter(object):
        """ Statistic counters for cache entries. """
        __slots__ = ['hit', 'miss', 'err']
    
        def __init__(self):
            self.hit = 0
            self.miss = 0
            self.err = 0
    
        @property
        def ratio(self):
            return 100.0 * self.hit / (self.hit + self.miss or 1)
    

    这里创建了一个用来记录每个方法缓存情况的对象,因为对于需要每个缓存的方法,都会创建一个该实例来记录缓存的状况(比如缓存用到或没用的次数等),所以为了节省内存加快访问速度这里指定了该对象拥有的三个属性。

    测试

    访问速度测试

    timeit是python一个用来简单测试运行时间的模块,详细可参见官方文档

    # In Python2.7
    
    # test1.py
    import timeit
    
    class Foo(object): __slots__ = 'foo',
    
    class Bar(object): pass
    
    slotted = Foo()
    not_slotted = Bar()
    
    def get_set_delete_fn(obj):
        def get_set_delete():
            obj.foo = 'foo'
            obj.foo
            del obj.foo
        return get_set_delete
    
    
    # In REPL
    >>> from test1 import *
    >>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
    min(timeit.repeat(get_set_delete_fn(not_slotted)))
    0.24305510520935059
    >>> min(timeit.repeat(get_set_delete_fn(slotted)))
    min(timeit.repeat(get_set_delete_fn(slotted)))
    0.21287798881530762
    

    可以看见,使用__slots__的对象有更快的访问速度,虽然在python2.7中差别没有在python3中那么明显

    内存占用参考

    关于内存占用情况的测试我还没测,但可以参考 stackoverflow上的测试,我这里机(无)智(耻)地取个结果:

    # 单位 bytes
    attrs  __slots__    no slots declared + __dict__
    none       16        64 (+ 280 if __dict__ referenced)
    one        56        64 + 280
    two        64        64 + 280
    six        96        64 + 1048
    22        224        64 + 3352
    

    可以明显看到内存占用减少的情况。

    注意事项

    __dict__可以理解成类里面存储属性的字典,

    1. 当一个类A继承自一个没有定义__slots__的类B时,A是有__dict__属性,这是再定义__slots__属性没有意义, 不能达到限制内存的作用
    2. 当尝试给一个定义了__slots__的类,而没有定义__dict__的类设置不在__slots__指定的那些属性时,会导致一个AttributeError
      其它注意请参照文档
  • 相关阅读:
    Map的两张遍历方法 keySet(),entrySet()
    http://localhost:8080请求用户名和密码。信息为:“XDB” 解决办法
    redis 集群出现的错误
    通信 缩略词
    redis
    hadoop 概念
    mysql 集群的一些概念
    SQL 基本关键字 函数 关联 连接
    JAVA集合
    SQL.Cookbook 读书笔记5 元数据查询
  • 原文地址:https://www.cnblogs.com/nisen/p/6036961.html
Copyright © 2011-2022 走看看