歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Python中類的__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
    其它注意請參照文檔

Copyright © Linux教程網 All Rights Reserved