代码之家  ›  专栏  ›  技术社区  ›  Jeb

使用__插槽__?

  •  1185
  • Jeb  · 技术社区  · 17 年前

    __slots__ 在Python中,尤其是关于我什么时候想使用它,什么时候不想使用它?

    14 回复  |  直到 7 年前
        1
  •  1356
  •   Aaron Hall    5 年前

    在Python中,目的是什么 __slots__ 应该避免哪些情况?

    太长,读不下去了

    特殊属性 __slots__ 允许您明确说明您希望对象实例具有哪些实例属性,并给出预期结果:

    1. 属性访问。
    2. 节省空间 在记忆中。

    1. __dict__ .
    2. 否认 __dict__ __weakref__ 如果父类拒绝它们并且您声明 __插槽__ .

    快速警告

    小警告,您应该只在继承树中声明一次特定的槽位。例如:

    class Base:
        __slots__ = 'foo', 'bar'
    
    class Right(Base):
        __slots__ = 'baz', 
    
    class Wrong(Base):
        __slots__ = 'foo', 'bar', 'baz'        # redundant foo and bar
    

    当你犯了这个错误时,Python不会反对(它可能应该反对),否则问题可能不会显现,但你的对象会占用比它们应该占用的更多的空间。Python 3.8:

    >>> from sys import getsizeof
    >>> getsizeof(Right()), getsizeof(Wrong())
    (56, 72)
    

    这是因为Base的插槽描述符有一个与Wrong分开的插槽。这通常不应该出现,但可能会:

    >>> w = Wrong()
    >>> w.foo = 'foo'
    >>> Base.foo.__get__(w)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: foo
    >>> Wrong.foo.__get__(w)
    'foo'
    

    最大的警告是多重继承——多个“具有非空槽的父类”不能组合在一起。

    为了适应这一限制,请遵循最佳实践:排除除一个或所有父类之外的所有抽象,它们的具体类和新的具体类将共同继承这些抽象——给抽象空槽(就像标准库中的抽象基类一样)。

    请参阅下面关于多重继承的部分以获取示例。

    要求:

    • __插槽__ 实际上存储在插槽中,而不是 字典 ,类必须继承自 object (在Python 3中是自动的,但在Python 2中必须是显式的)。

    • 为了防止创建 字典 ,你必须继承自 对象 继承中的所有类都必须声明 __插槽__ 他们都不能有 '__dict__' 进入。

    如果你想继续阅读,有很多细节。

    为什么使用 __插槽__ :更快的属性访问。

    Python的创建者Guido van Rossum, states __插槽__ 以实现更快的属性访问。

    证明明显更快的访问是微不足道的:

    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
    

    >>> min(timeit.repeat(get_set_delete_fn(slotted)))
    0.2846834529991611
    >>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
    0.3664822799983085
    

    在Ubuntu上的Python 3.5中,时隙访问速度快了近30%。

    >>> 0.3664822799983085 / 0.2846834529991611
    1.2873325658284342
    

    在Windows上的Python 2中,我测得它快了大约15%。

    为什么使用 __插槽__ :节省内存

    另一个目的 __插槽__ 是为了减少每个对象实例占用的内存空间。

    My own contribution to the documentation clearly states the reasons behind this :

    使用后节省的空间 字典 可能很重要。

    SQLAlchemy attributes 节省大量内存 __插槽__ .

    为了验证这一点,在Ubuntu Linux上使用Python 2.7的Anaconda发行版 guppy.hpy (又名heapy)和 sys.getsizeof ,类实例的大小没有 __插槽__ 声明的,而不是其他,是64个字节。确实如此 包括 字典 再次感谢Python的懒惰求值 字典 在被引用之前,它显然不会被调用为存在,但没有数据的类通常是无用的。当被召唤时 字典 属性至少为280字节。

    __插槽__ 宣布为 () (无数据)只有16个字节,插槽中有一个项目,总共56个字节,有两个项目,64个字节。

    对于64位Python,我以字节为单位说明了Python 2.7和3.6中的内存消耗 __插槽__ 字典

           Python 2.7             Python 3.6
    attrs  __slots__  __dict__*   __slots__  __dict__* | *(no slots defined)
    none   16         56 + 272†   16         56 + 112† | †if __dict__ referenced
    one    48         56 + 272    48         56 + 112
    two    56         56 + 272    56         56 + 112
    six    88         56 + 1040   88         56 + 152
    11     128        56 + 1040   128        56 + 240
    22     216        56 + 3344   216        56 + 408     
    43     384        56 + 3344   384        56 + 752
    

    因此,尽管Python 3中的字典较小,但我们看到它有多好 __插槽__ 缩放实例以节省内存,这是您想要使用的主要原因 __插槽__ .

    为了使我的笔记更完整,请注意,在Python 2中,类名称空间中的每个插槽的一次性成本为64个字节,在Python 3中为72个字节,因为插槽使用像属性这样的数据描述符,称为“成员”。

    >>> Foo.foo
    <member 'foo' of 'Foo' objects>
    >>> type(Foo.foo)
    <class 'member_descriptor'>
    >>> getsizeof(Foo.foo)
    72
    

    __插槽__ :

    否认创造 字典 ,您必须子类化 对象 所有子类 对象 在Python 3中,但在Python 2中,你必须明确:

    class Base(object): 
        __slots__ = ()
    

    现在:

    >>> b = Base()
    >>> b.a = 'a'
    Traceback (most recent call last):
      File "<pyshell#38>", line 1, in <module>
        b.a = 'a'
    AttributeError: 'Base' object has no attribute 'a'
    

    或者子类化另一个定义 __插槽__

    class Child(Base):
        __slots__ = ('a',)
    

    现在:

    c = Child()
    c.a = 'a'
    

    但是:

    >>> c.b = 'b'
    Traceback (most recent call last):
      File "<pyshell#42>", line 1, in <module>
        c.b = 'b'
    AttributeError: 'Child' object has no attribute 'b'
    

    允许 字典 在创建子类化开槽对象的同时,只需添加 字典 __插槽__ (请注意,插槽是有序的,您不应该重复已经在父类中的插槽):

    class SlottedWithDict(Child): 
        __slots__ = ('__dict__', 'b')
    
    swd = SlottedWithDict()
    swd.a = 'a'
    swd.b = 'b'
    swd.c = 'c'
    

    >>> swd.__dict__
    {'c': 'c'}
    

    或者你甚至不需要申报 __插槽__ 在你的子类中,你仍然会使用父类中的插槽,但不会限制创建 字典 :

    class NoSlots(Child): pass
    ns = NoSlots()
    ns.a = 'a'
    ns.b = 'b'
    

    以及:

    >>> ns.__dict__
    {'b': 'b'}
    

    然而, __插槽__ 可能会导致多重继承问题:

    class BaseA(object): 
        __slots__ = ('a',)
    
    class BaseB(object): 
        __slots__ = ('b',)
    

    因为从具有两个非空插槽的父类创建子类失败:

    >>> class Child(BaseA, BaseB): __slots__ = ()
    Traceback (most recent call last):
      File "<pyshell#68>", line 1, in <module>
        class Child(BaseA, BaseB): __slots__ = ()
    TypeError: Error when calling the metaclass bases
        multiple bases have instance lay-out conflict
    

    如果你遇到这个问题,你 可能会 只需删除 __插槽__ 从父节点开始,或者如果你可以控制父节点,给它们空的插槽,或者重构为抽象:

    from abc import ABC
    
    class AbstractA(ABC):
        __slots__ = ()
    
    class BaseA(AbstractA): 
        __slots__ = ('a',)
    
    class AbstractB(ABC):
        __slots__ = ()
    
    class BaseB(AbstractB): 
        __slots__ = ('b',)
    
    class Child(AbstractA, AbstractB): 
        __slots__ = ('a', 'b')
    
    c = Child() # no problem!
    

    添加 字典 __插槽__ 要获得动态分配:

    class Foo(object):
        __slots__ = 'bar', 'baz', '__dict__'
    

    现在:

    >>> foo = Foo()
    >>> foo.boink = 'boink'
    

    所以与 字典 在插槽中,我们失去了一些大小优势,但有动态分配的好处,而且仍然有我们期望的名称插槽。

    当你从一个没有开槽的对象继承时,当你使用 __插槽__ -中的名称 __插槽__ 指向开槽值,而任何其他值都放在实例的 字典 .

    __插槽__ 因为你想能够动态添加属性实际上并不是一个好理由——只需添加 "__dict__" 到你的 __插槽__ 如果需要的话。

    您可以类似地添加 __weakref__ __插槽__ 如果你需要这个功能。

    在对namedtuple进行子类化时设置为空tuple:

    内置的namedtuple创建了非常轻量级的不可变实例(本质上是元组的大小),但为了获得好处,如果你对它们进行子类化,你需要自己做:

    from collections import namedtuple
    class MyNT(namedtuple('MyNT', 'bar baz')):
        """MyNT is an immutable and lightweight object"""
        __slots__ = ()
    

    用法:

    >>> nt = MyNT('bar', 'baz')
    >>> nt.bar
    'bar'
    >>> nt.baz
    'baz'
    

    试图分配一个意外的属性会引发 AttributeError 因为我们阻止了 字典 :

    >>> nt.quux = 'quux'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'MyNT' object has no attribute 'quux'
    

    字典 __slots__ = () ,但不能使用非空 __插槽__ 带有元组的子类型。

    最大的隐患:多重继承

    即使多个父节点的非空插槽相同,它们也不能一起使用:

    class Foo(object): 
        __slots__ = 'foo', 'bar'
    class Bar(object):
        __slots__ = 'foo', 'bar' # alas, would work if empty, i.e. ()
    
    >>> class Baz(Foo, Bar): pass
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: Error when calling the metaclass bases
        multiple bases have instance lay-out conflict
    

    使用空 __插槽__ 父母似乎提供了最大的灵活性, (通过添加 字典 要获得动态分配,请参阅上文) 创建a 字典 :

    class Foo(object): __slots__ = ()
    class Bar(object): __slots__ = ()
    class Baz(Foo, Bar): __slots__ = ('foo', 'bar')
    b = Baz()
    b.foo, b.bar = 'foo', 'bar'
    

    你没有 要有插槽,所以如果你添加它们,稍后再删除它们,应该不会造成任何问题。

    在这里冒险 如果你在作曲 mixins 或使用 abstract base classes ,不打算实例化,一个空 __插槽__ 在这些方面,父母似乎是子群体灵活性的最佳选择。

    为了演示,首先,让我们用我们想在多重继承下使用的代码创建一个类

    class AbstractBase:
        __slots__ = ()
        def __init__(self, a, b):
            self.a = a
            self.b = b
        def __repr__(self):
            return f'{type(self).__name__}({repr(self.a)}, {repr(self.b)})'
    

    我们可以通过继承和声明预期的插槽来直接使用上面的内容:

    class Foo(AbstractBase):
        __slots__ = 'a', 'b'
    

    但我们不在乎这一点,这是微不足道的单继承,我们需要另一个我们也可能从中继承的类,也许有一个嘈杂的属性:

    class AbstractBaseC:
        __slots__ = ()
        @property
        def c(self):
            print('getting c!')
            return self._c
        @c.setter
        def c(self, arg):
            print('setting c!')
            self._c = arg
    

    现在,如果两个基地都有非空的位置,我们就不能做下面的事情。(事实上,如果我们愿意,我们可以给 AbstractBase 非空插槽a和b,并将它们排除在下面的声明之外——将它们留在其中是错误的):

    class Concretion(AbstractBase, AbstractBaseC):
        __slots__ = 'a b _c'.split()
    

    现在我们通过多重继承从两者中获得了功能,并且仍然可以拒绝 字典 __weakref__ 实例化:

    >>> c = Concretion('a', 'b')
    >>> c.c = c
    setting c!
    >>> c.c
    getting c!
    Concretion('a', 'b')
    >>> c.d = 'd'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Concretion' object has no attribute 'd'
    

    其他避免插槽的情况:

    • 当你想表演时,避开它们 __class__ 除非插槽布局相同,否则使用另一个没有它们的类进行分配(并且您无法添加它们)。(我很想知道谁在做这件事,为什么。)
    • 如果你想将long、tuple或str等可变长度内置子类化,并且想向它们添加属性,请避免使用它们。
    • 如果您坚持通过实例变量的类属性提供默认值,请避免使用它们。

    __插槽__ documentation (the 3.7 dev docs are the most current) 我最近对此做出了重大贡献。

    对其他答案的批评

    目前排名靠前的答案引用了过时的信息,而且波浪很大,在一些重要方面没有达到标准。

    不要“只使用 __插槽__ 当实例化大量对象时”

    我引述:

    “你会想用 __插槽__ 如果你要实例化同一类的大量(数百、数千)对象。"

    例如,抽象基类 collections 模块尚未实例化 __插槽__ 为他们宣布。

    如果用户希望拒绝 字典 __weakref__ 创建时,这些东西不能在父类中可用。

    __插槽__ 在创建接口或混入时有助于可重用性。

    诚然,许多Python用户并不是为了可重用性而写作,但当你这样做的时候,有权拒绝不必要的空间使用是有价值的。

    __插槽__

    TypeError :

    >>> pickle.loads(pickle.dumps(f))
    TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
    

    这实际上是不正确的。此消息来自最旧的协议,这是默认协议。您可以使用选择最新协议 -1 争论。在Python 2.7中,这将是 2 (在2.3中介绍),在3.6中是 4 .

    >>> pickle.loads(pickle.dumps(f, -1))
    <__main__.Foo object at 0x1129C770>
    

    >>> pickle.loads(pickle.dumps(f, 2))
    <__main__.Foo object at 0x1129C770>
    

    Python 3.6

    >>> pickle.loads(pickle.dumps(f, 4))
    <__main__.Foo object at 0x1129C770>
    

    所以我会记住这一点,因为这是一个已解决的问题。

    对(截至2016年10月2日)已接受答案的批评

    第一段一半是简短的解释,一半是预测性的。这是唯一真正回答这个问题的部分

    正确使用 __插槽__

    下半部分是一厢情愿的想法,而且不切题:

    虽然这有时是一个有用的优化,但如果Python解释器足够动态,只在对象实际添加时才需要字典,那么这是完全没有必要的。

    Python实际上做了类似的事情,只是创建了 字典 当它被访问时,创建大量没有数据的对象是相当荒谬的。

    第二段过于简单化,忽略了需要避免的实际原因 __插槽__ .下面是 避免插槽的真正原因( 实际的 原因,请参阅上面我的其余答案。):

    它们改变了具有插槽的对象的行为,这可能会被控制狂和静态打字的小家伙滥用。

    __插槽__ .

    第三段更是一厢情愿。总的来说,这些内容大多不符合标准,回答者甚至没有撰写,并为该网站的批评者提供了弹药。

    内存使用情况证据

    创建一些普通对象和开槽对象:

    >>> class Foo(object): pass
    >>> class Bar(object): __slots__ = ()
    

    实例化一百万个:

    >>> foos = [Foo() for f in xrange(1000000)]
    >>> bars = [Bar() for b in xrange(1000000)]
    

    检查 guppy.hpy().heap() :

    >>> guppy.hpy().heap()
    Partition of a set of 2028259 objects. Total size = 99763360 bytes.
     Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
         0 1000000  49 64000000  64  64000000  64 __main__.Foo
         1     169   0 16281480  16  80281480  80 list
         2 1000000  49 16000000  16  96281480  97 __main__.Bar
         3   12284   1   987472   1  97268952  97 str
    ...
    

    访问常规对象及其 字典 并再次检查:

    >>> for f in foos:
    ...     f.__dict__
    >>> guppy.hpy().heap()
    Partition of a set of 3028258 objects. Total size = 379763480 bytes.
     Index  Count   %      Size    % Cumulative  % Kind (class / dict of class)
         0 1000000  33 280000000  74 280000000  74 dict of __main__.Foo
         1 1000000  33  64000000  17 344000000  91 __main__.Foo
         2     169   0  16281480   4 360281480  95 list
         3 1000000  33  16000000   4 376281480  99 __main__.Bar
         4   12284   0    987472   0 377268952  99 str
    ...
    

    这与Python的历史是一致的,从 Unifying types and classes in Python 2.2

    如果你子类化了一个内置类型,额外的空间会自动添加到实例中以容纳 字典 __weakrefs__ (The 字典 在您使用它之前,它不会被初始化,因此您不应该担心您创建的每个实例的空字典所占用的空间。)如果你不需要这个额外的空间,你可以添加短语“ __slots__ = [] “去你的班。

        2
  •  268
  •   Rob Bednark Bohdan    9 年前

    报价 Jacob Hallen :

    正确使用 __slots__ 是为了节省物体的空间。而不是拥有 允许随时向对象添加属性的动态字典, 存在不允许在创建后添加的静态结构。 [此用法 __插槽__ 消除了每个对象一个字典的开销。]虽然这有时是一个有用的优化,但它将完全 如果Python解释器足够动态 只有在对象实际有添加时才需要dict。

    不幸的是,老虎机也有副作用。他们改变了行为 具有插槽的对象可能会被控制狂滥用 还有静态打字。这很糟糕,因为控制狂应该 装饰器,因为在Python中,应该只有一种明显的方法来做某事。

    使CPython足够智能,可以在不占用空间的情况下处理节省空间的问题 __插槽__ 是一个专业 这可能就是为什么它还没有出现在P3k的更改列表中。

        3
  •  133
  •   Toby Speight    6 年前

    你会想使用 __slots__ 如果你要实例化同一类的大量(数百、数千)对象。 __插槽__

    强烈建议不要使用 __插槽__ 用于约束属性创建。

    酸洗物体 __插槽__ 不适用于默认(最旧)的pickle协议;有必要指定一个更高的版本。

    python的其他一些自省特性也可能受到不利影响。

        4
  •  68
  •   muhuk    15 年前

    每个python对象都有一个 __dict__ attribute是一个包含所有其他属性的字典。例如,当你打字时 self.attr python实际上正在做 self.__dict__['attr'] 正如你所想象的,使用字典存储属性需要一些额外的空间&;是时候访问它了。

    但是,当您使用 __slots__ ,为该类创建的任何对象都不会有 字典 属性。相反,所有属性访问都是直接通过指针完成的。

    因此,如果你想要一个C风格的结构而不是一个完整的类,你可以使用 __插槽__ 用于压缩物体的尺寸和;减少属性访问时间。一个很好的例子是包含属性x&y.如果你想获得很多分数,你可以尝试使用 __插槽__ 为了节省一些内存。

        5
  •  27
  •   Evgeni Sergeev    11 年前

    除了其他答案,这里还有一个使用 __slots__ :

    >>> class Test(object):   #Must be new-style class!
    ...  __slots__ = ['x', 'y']
    ... 
    >>> pt = Test()
    >>> dir(pt)
    ['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', 
     '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', 
     '__repr__', '__setattr__', '__slots__', '__str__', 'x', 'y']
    >>> pt.x
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: x
    >>> pt.x = 1
    >>> pt.x
    1
    >>> pt.z = 2
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Test' object has no attribute 'z'
    >>> pt.__dict__
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Test' object has no attribute '__dict__'
    >>> pt.__slots__
    ['x', 'y']
    

    因此,为了实施 __插槽__ ,它只需要额外的一行(如果你的类还没有成为新型类,那么就把它变成一个新型类)。这样你就可以 reduce the memory footprint of those classes 5-fold ,代价是必须编写自定义pickle代码,如果必要的话。

        6
  •  14
  •   Demolishun    13 年前

    documentation 对于希望使用插槽减少常用函数的函数开销的高性能库来说,速度要快得多。

    现在,这可能与OP问题没有直接关系。它更多地与建筑扩展有关,而不是与使用 __slots__ 对象上的语法。但它确实有助于完整了解插槽的使用情况及其背后的一些推理。

        7
  •  9
  •   Dmitry Rubanovich    12 年前

    __slot__ 属性。

    问题:没有 __slots__

    如果我没有 __插槽__ 在我的类中,我可以向我的对象添加新的属性。

    class Test:
        pass
    
    obj1=Test()
    obj2=Test()
    
    print(obj1.__dict__)  #--> {}
    obj1.x=12
    print(obj1.__dict__)  # --> {'x': 12}
    obj1.y=20
    print(obj1.__dict__)  # --> {'x': 12, 'y': 20}
    
    obj2.x=99
    print(obj2.__dict__)  # --> {'x': 99}
    

    如果你看上面的例子,你可以看到 obj1 obj2 有自己的 英语字母表的第24个字母 attributes和python还创建了一个 dict 每个对象的属性( obj1 obj2 ).

    假设我的班级 测试 有成千上万的这样的物品吗?创建附加属性 字典 因为每个对象都会在我的代码中造成大量开销(内存、计算能力等)。

    解决方案:使用 __插槽__

    现在在下面的例子中,我的类 测试 包含 __插槽__ 属性。现在,我无法向对象添加新属性(属性除外 x 字典 属性不再。这消除了每个对象的开销,如果您有许多对象,开销可能会变得很大。

    class Test:
        __slots__=("x")
    
    obj1=Test()
    obj2=Test()
    obj1.x=12
    print(obj1.x)  # --> 12
    obj2.x=99
    print(obj2.x)  # --> 99
    
    obj1.y=28
    print(obj1.y)  # --> AttributeError: 'Test' object has no attribute 'y'
    
        8
  •  7
  •   Community Mohan Dere    6 年前

    在……里面 常规属性访问 ,实例充当字典,属性的名称充当该字典中查找值的键。

    实例(属性)-->值

    在……里面 __插槽_访问 ,属性的名称充当字典,实例充当字典查找值中的键。

    属性(实例)-->值

    在……里面 飞锤图案 ,属性的名称充当字典,值充当查找实例的字典中的键。

    属性(值)-->例子

        9
  •  3
  •   NeilenMarais    9 年前

    dict 可用于通过以下方式向属性添加描述 __slots__ . None

    class Person:
    
        __slots__ = {
            "birthday":
                "A datetime.date object representing the person's birthday.",
            "name":
                "The first and last name.",
            "public_variable":
                None,
            "_private_variable":
                "Description",
        }
    
    
    help(Person)
    """
    Help on class Person in module __main__:
    
    class Person(builtins.object)
     |  Data descriptors defined here:
     |
     |  birthday
     |      A datetime.date object representing the person's birthday.
     |
     |  name
     |      The first and last name.
     |
     |  public_variable
    """
    
        10
  •  2
  •   Noctis Skytower    9 年前

    除了其他答案之外, __slots__ 通过将属性限制在预定义的列表中,还增加了一点排版安全性。这一直是JavaScript的一个问题,它还允许您向现有对象添加新属性,无论您是否有意。

    这是一个普通的无槽对象,它什么也不做,但允许您添加属性:

    class Unslotted:
        pass
    test = Unslotted()
    test.name = 'Fred'
    test.Name = 'Wilma'
    

    由于Python区分大小写,拼写相同但大小写不同的两个属性是不同的。如果你怀疑其中一个是打字错误,那就倒霉了。

    使用插槽,您可以限制这一点:

    class Slotted:
        __slots__ = ('name')
        pass
    test = Slotted()
    test.name = 'Fred'      #   OK
    test.Name = 'Wilma'     #   Error
    

    这一次,第二个属性( Name __插槽__ 收藏。

    我建议使用它可能更好 __插槽__ 在可能的情况下保持对对象的更多控制。

        11
  •  1
  •   Red Pill    10 年前

    另一个稍显隐晦的用法 __slots__ 是从ProxyTypes包(以前是PEAK项目的一部分)向对象代理添加属性。它的 ObjectWrapper 允许您代理另一个对象,但拦截与被代理对象的所有交互。它不是很常用(也不支持Python 3),但我们用它来实现一个基于tornado的异步实现周围的线程安全阻塞包装器,该包装器使用线程安全机制通过ioloop反弹对代理对象的所有访问 concurrent.Future 对象以同步和返回结果。

    默认情况下,对代理对象的任何属性访问都将为您提供代理对象的结果。如果需要在代理对象上添加属性, __插槽__ 可以使用。

    from peak.util.proxies import ObjectWrapper
    
    class Original(object):
        def __init__(self):
            self.name = 'The Original'
    
    class ProxyOriginal(ObjectWrapper):
    
        __slots__ = ['proxy_name']
    
        def __init__(self, subject, proxy_name):
            # proxy_info attributed added directly to the
            # Original instance, not the ProxyOriginal instance
            self.proxy_info = 'You are proxied by {}'.format(proxy_name)
    
            # proxy_name added to ProxyOriginal instance, since it is
            # defined in __slots__
            self.proxy_name = proxy_name
    
            super(ProxyOriginal, self).__init__(subject)
    
    if __name__ == "__main__":
        original = Original()
        proxy = ProxyOriginal(original, 'Proxy Overlord')
    
        # Both statements print "The Original"
        print "original.name: ", original.name
        print "proxy.name: ", proxy.name
    
        # Both statements below print 
        # "You are proxied by Proxy Overlord", since the ProxyOriginal
        # __init__ sets it to the original object 
        print "original.proxy_info: ", original.proxy_info
        print "proxy.proxy_info: ", proxy.proxy_info
    
        # prints "Proxy Overlord"
        print "proxy.proxy_name: ", proxy.proxy_name
        # Raises AttributeError since proxy_name is only set on 
        # the proxy object
        print "original.proxy_name: ", proxy.proxy_name
    
    推荐文章