__slots__
的做用是阻止在實例化類時爲實例分配dict,默認狀況下每一個類都會有一個dict,經過__dict__
訪問,這個dict維護了這個實例的全部屬性。html
代碼:java
# coding:utf-8 class Base(object): val = 1 def __init__(self): pass class BaseSlots(object): val = 1 __slots__ = ('y',) def __init__(self): pass class BaseSlots2(object): val = 1 y = 2 __slots__ = ('y',) def __init__(self): pass b1 = Base() print 'b1.__dict__ is ', b1.__dict__ # b1.__dict__ is {} b1.x = 1 print 'bi.x = 1, b1.__dict__ is ', b1.__dict__ # bi.x = 1, b1.__dict__ is {'x': 1} b2 = BaseSlots() print 'b2.__dict__ is ', b2.__dict__ # AttributeError: 'BaseSlots' object has no attribute '__dict__' b2.x = 1 # AttributeError: 'BaseSlots2' object has no attribute 'x' b2.y = 3 print 'b2.__dict__ is ', b2.__dict__ # AttributeError: 'BaseSlots' object has no attribute '__dict__' b3 = BaseSlots2() print 'b3.__dict__ is ', b3.__dict__ # AttributeError: 'BaseSlots2' object has no attribute '__dict__' b3.x = 1 # AttributeError: 'BaseSlots2' object has no attribute 'x' b3.y = 3 # 'BaseSlots2' object attribute 'y' is read-only print 'b3.__dict__ is ', b3.__dict__ # AttributeError: 'BaseSlots2' object has no attribute '__dict__'
輸出python
# Base()輸出 b1.__dict__ is {} bi.x = 1, b1.__dict__ is {'x': 1} # BaseSlots()輸出 b2.__dict__ is Traceback (most recent call last): File "test04.py", line 34, in <module> print 'b2.__dict__ is ', b2.__dict__ AttributeError: 'BaseSlots' object has no attribute '__dict__' Traceback (most recent call last): File "test04.py", line 35, in <module> b2.x = 1 AttributeError: 'BaseSlots' object has no attribute 'x' b2.__dict__ is Traceback (most recent call last): File "C:/Users/fred1/PycharmProjects/test/test04.py", line 37, in <module> print 'b2.__dict__ is ', b2.__dict__ AttributeError: 'BaseSlots' object has no attribute '__dict__' # BaseSlots2輸出 File "test04.py", line 40, in <module> print 'b3.__dict__ is ', b3.__dict__ AttributeError: 'BaseSlots2' object has no attribute '__dict__' Traceback (most recent call last): File "test04.py", line 41, in <module> b3.x = 1 AttributeError: 'BaseSlots2' object has no attribute 'x' Traceback (most recent call last): File "test04.py", line 42, in <module> b3.y = 3 AttributeError: 'BaseSlots2' object attribute 'y' is read-only Traceback (most recent call last): File "test04.py", line 43, in <module> print 'b3.__dict__ is ', b3.__dict__ AttributeError: 'BaseSlots2' object has no attribute '__dict__'
可見:實例的 __dict__
只保持實例的變量,對於類的屬性是不保存的,類的屬性包括變量和函數。因爲每次實例化一個類都要分配一個新的dict,所以存在空間的浪費,所以有了__slots__
,當定義了__slots__
後,__slots__
中定義的變量變成了類的描述符,至關於java,c++中的成員變量聲明,類的實例只能擁有這些個變量,而不在有__dict__
,所以也就不能在增長新的變量。c++
Python 是一門動態語言,能夠在運行過程當中,修改對象的屬性和添加修改方法。任何類的實例對象包含一個字典__dict__
(類型爲dictproxy), Python經過這個字典將任意屬性綁定到對象上。有時候咱們只想使用固定的對象,而不想任意綁定對象,這時候咱們能夠定義一個屬性名稱集合,只有在這個集合裏的名稱才能夠綁定。__slots__
就是完成這個功能的。web
使用__slots__
的主要緣由是當你只須要用預約義一系列屬性的簡單對象,而且不須要攜帶__dict__方法時來節省空間。_PS:僅在你有大量實例的時候使用。_app
# coding:utf-8 import sys import pympler.asizeof as sf # Pympler is a development tool to measure, monitor and analyze the memory behavior of Python objects in a running Python application. class Slots(object): pass class WithSlots(object): __slots__ = ('a', 'b', 'c') pass n = Slots() n.a, n.b, n.c = 1, 2, 3 w = WithSlots() w.a, w.b, w.c = 1, 2, 3 print sys.getsizeof(n) # 32 print sys.getsizeof(w) # 36 print sf.asizeof(n) # 296 print sf.asizeof(w) # 136 # test in Python 2.7.10
__slots__
容許子類重複繼承less
# coding:utf-8 import sys import pympler.asizeof as sf class A(object): __slots__ = 'a' pass class AB(A): __slots__ = 'b' pass ab = AB() ab.a = ab.b = 23 class ABC(A): __slots__ = 'a', 'b' # 容許重複繼承 pass abc = ABC() abc.a = abc.b = 23 print sf.asizeof(ab) # 88 print sf.asizeof(abc) # 96 # test in Python 2.7.10
若子類沒有__slots__
,父類的__slots__
對子類無效。ide
>>> class A(object): __slots__ = 'a' ... >>> a = A() >>> a.b = 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'A' object has no attribute 'b' >>> class B(A): pass ... >>> b = B() >>> b.b = 2 >>> b.b 2 >>>
Without a __dict__
variable, instances cannot be assigned new variables not listed in the __slots__
definition. Attempts to assign to an unlisted variable name raises AttributeError
. If dynamic assignment of new variables is desired, then add __dict__
to the sequence of strings in the __slots__
declaration. Changed in version 2.3: Previously, adding __dict__
to the __slots__
declaration would not enable the assignment of new attributes not specifically listed in the sequence of instance variable names.函數
Without a__weakref__
variable for each instance, classes defining __slots__
do not support weak references to its instances. If weak reference support is needed, then add __weakref__
to the sequence of strings in the __slots__
declaration. Changed in version 2.3: Previously, adding __weakref__
to the __slots__
declaration would not enable support for weak references.ui
__slots__
are implemented at the class level by creating descriptors (3.4.2) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by __slots__
; otherwise, the class attribute would overwrite the descriptor assignment.
If a class defines a slot also defined in a base class, the instance variable defined by the base class slot is inaccessible (except by retrieving its descriptor directly from the base class). This renders the meaning of the program undefined. In the future, a check may be added to prevent this.
The action of a __slots__
declaration is limited to the class where it is defined. As a result, subclasses will have a __dict__
unless they also define __slots__
.
__slots__
do not work for classes derived from ``variable-length'' built-in types such as long, str and tuple.
Any non-string iterable may be assigned to __slots__
. Mappings may also be used; however, in the future, special meaning may be assigned to the values corresponding to each key.
We’ve mentioned before how Oyster.com’s Python-based web servers cache huge amounts of static content in huge Python dicts (hash tables). Well, we recently saved over 2 GB in each of four 6 GB server processes with a single line of code — using __slots__
on our Image
class.
Here’s a screenshot of RAM usage before and after deploying this change on one of our servers:
We allocate about a million instances of a class like the following:
class Image(object): def __init__(self, id, caption, url): self.id = id self.caption = caption self.url = url self._setup() # ... other methods ...
By default Python uses a dict to store an object’s instance attributes. Which is usually fine, and it allows fully dynamic things like setting arbitrary new attributes at runtime.
However, for small classes that have a few fixed attributes known at 「compile time」, the dict is a waste of RAM, and this makes a real difference when you’re creating a million of them. You can tell Python not to use a dict, and only allocate space for a fixed set of attributes, by settings __slots__
on the class to a fixed list of attribute names:
class Image(object): __slots__ = ['id', 'caption', 'url'] def __init__(self, id, caption, url): self.id = id self.caption = caption self.url = url self._setup() # ... other methods ...
Note that you can also use collections.namedtuple, which allows attribute access, but only takes the space of a tuple, so it’s similar to using __slots__
on a class. However, to me it always feels weird to inherit from a namedtuple class. Also, if you want a custom initializer you have to override __new__
rather than __init__
.
Warning: Don’t prematurely optimize and use this everywhere! It’s not great for code maintenance, and it really only saves you when you have thousands of instances.