资讯专栏INFORMATION COLUMN

Python的__slots__

wenhai.he / 1200人阅读

摘要:任何类的实例对象包含一个字典类型为,通过这个字典将任意属性绑定到对象上。就是完成这个功能的。允许子类重复继承允许重复继承若子类没有,父类的对子类无效。

__slots__ 用处

__slots__的作用是阻止在实例化类时为实例分配dict,默认情况下每个类都会有一个dict,通过__dict__访问,这个dict维护了这个实例的所有属性。

代码:

# 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__"

输出

# 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 
    print "b2.__dict__ is ", b2.__dict__
AttributeError: "BaseSlots" object has no attribute "__dict__"

Traceback (most recent call last):
  File "test04.py", line 35, in 
    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 
    print "b2.__dict__ is ", b2.__dict__
AttributeError: "BaseSlots" object has no attribute "__dict__"

# BaseSlots2输出
  File "test04.py", line 40, in 
    print "b3.__dict__ is ", b3.__dict__
AttributeError: "BaseSlots2" object has no attribute "__dict__"

Traceback (most recent call last):
  File "test04.py", line 41, in 
    b3.x = 1
AttributeError: "BaseSlots2" object has no attribute "x"

Traceback (most recent call last):
  File "test04.py", line 42, in 
    b3.y = 3
AttributeError: "BaseSlots2" object attribute "y" is read-only

Traceback (most recent call last):
  File "test04.py", line 43, in 
    print "b3.__dict__ is ", b3.__dict__
AttributeError: "BaseSlots2" object has no attribute "__dict__"

可见:实例的 __dict__ 只保持实例的变量,对于类的属性是不保存的,类的属性包括变量和函数。由于每次实例化一个类都要分配一个新的dict,因此存在空间的浪费,因此有了__slots__,当定义了__slots__后,__slots__中定义的变量变成了类的描述符,相当于java,c++中的成员变量声明,类的实例只能拥有这些个变量,而不在有__dict__,因此也就不能在增加新的变量。

Python 是一门动态语言,可以在运行过程中,修改对象的属性和添加修改方法。任何类的实例对象包含一个字典__dict__ (类型为dictproxy), Python通过这个字典将任意属性绑定到对象上。有时候我们只想使用固定的对象,而不想任意绑定对象,这时候我们可以定义一个属性名称集合,只有在这个集合里的名称才可以绑定。__slots__就是完成这个功能的。

使用__slots__的主要原因是当你只需要用预定义一系列属性的简单对象,并且不需要携带__dict__方法时来节省空间。_PS:仅在你有大量实例的时候使用。_

# 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__允许子类重复继承

# 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__对子类无效。

>>> class A(object): __slots__ = "a"
...
>>> a = A()
>>> a.b = 2
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: "A" object has no attribute "b"
>>> class B(A): pass
...
>>> b = B()
>>> b.b = 2
>>> b.b
2
>>>
Notes on using slots

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.

__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.

扩展阅读
Saving 9 GB of RAM with Python’s slots

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.

参考

《python __slots__》

http://stackoverflow.com/questions/1816483/python-how-does-inheritance-of-slots-in-subclasses-actually-work

《Saving 9 GB of RAM with Python’s __slots__》

《使用__slots__》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/37883.html

相关文章

  • 如何优化Python占用内存

    摘要:概述如果程序处理的数据比较多比较复杂,那么在程序运行的时候,会占用大量的内存,当内存占用到达一定的数值,程序就有可能被操作系统终止,特别是在限制程序所使用的内存大小的场景,更容易发生问题。下面我就给出几个优化占用内存的几个方法。 概述 如果程序处理的数据比较多、比较复杂,那么在程序运行的时候,会占用大量的内存,当内存占用到达一定的数值,程序就有可能被操作系统终止,特别是在限制程序所使用...

    ThreeWords 评论0 收藏0
  • 通过Python__slots__节省9GB内存

    摘要:我们之前提到过,基于的服务器缓存了大量字典哈希表的静态内容。好啦,我们最近使用一行代码在我们的类中使用,使得每个的服务器在处理中都节省超过内存。一般说来,这很不错,而且还支持完全动态性,比如在运行时设置任意新的属性。 我们之前提到过,Oyster.com基于Python的web服务器缓存了大量Python字典(dicts)(哈希表(hash tables))的静态内容。好啦,我们最近使...

    EastWoodYang 评论0 收藏0
  • classmethod&staticmethod 以及 __slots__

    摘要:什么是中的它的用途是什么装饰器对应的函数不需要实例化,不需要参数,但第一个参数需要是表示自身类的参数,可以来调用类的属性,类的方法,实例化对象等。 什么是python中的classmethod,它的用途是什么? classmethod装饰器对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。 cl...

    newsning 评论0 收藏0
  • 编写符合Python风格对象

    摘要:自定义向量类型从自定义向量类型入手写出符合风格的对象,这离不开特殊方法的支持。将对象定为不可变的通过使用两个前导下划线。程序员约定使用一个下划线前缀编写受保护的属性即,他们认为应该使用命名约定来避免意外覆盖属性。 导语:本文章记录了本人在学习Python基础之面向对象篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 本文重点: 1、掌握编写Pythonic c...

    ethernet 评论0 收藏0
  • Python 面向对象编程OOP (二) slots,类多态,继承,复写方法

    摘要:需要注意的是的限定只对当前类的对象生效,对子类并不起任何作用。本文的实例名称均为杜撰,请不要对号入座我的其他文章已经放到了上,如果感兴趣的朋友可以去看看,链接如下精品练习题道实用技巧汇总教程 __slots__魔法 大家好,上一期我重点总结了有关类的基本知识,现在简单回顾一下,顺便加上一个创建类时常用的东西:__slots__ 首先创建一个名人类:Celebrity class Ce...

    Binguner 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<