通用代理和包装器类型。
proxytypes3的Python项目详细描述
简单代理类型
proxytypes3
包为创建
普通Python对象的代理和包装器。自动代理对象
将所有属性访问和操作委托给代理对象。包装纸
是相似的,但是可以子类化以允许附加属性和
要添加到包装对象的操作。
注意,这些代理类型不是有意篡改的;
可以使用代理的__subject__
轻松访问对象的形式。
属性,有些代理类型甚至允许设置此属性。(这个可以
对于那些懒散地创建循环结构并因此需要
能够发布“正向引用”代理。
这个包是peak.util.proxies模块的叉, 扩展到支持Python3.*以及更好的测试和持续集成。
代理基础知识
下面是ObjectProxy
类型的快速演示:
>>> from proxytypes3 import ObjectProxy
>>> p = ObjectProxy(42)
>>> p
42
>>> isinstance(p, int)
True
>>> p.__class__
<... 'int'>
>>> p*2
84
>>> 'X' * p
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
>>> hex(p)
'0x2a'
>>> chr(p)
'*'
>>> p ^ 1
43
>>> p ** 2
1764
如您所见,代理实际上与它的对象没有区别 代理,除了通过其^ {CD2>}属性,及其^ {CD5>}::
>>> p.__subject__
42
>>> type(p)
<class 'proxytypes3.proxies.ObjectProxy'>
您可以更改ObjectProxy
的__subject__
,然后它将
参考其他内容:
>>> p.__subject__ = 99
>>> p
99
>>> p-33
66
>>> p.__subject__ = "foo"
>>> p
'foo'
所有操作都委托给主题,包括setattr和delattr::
>>> class Dummy: pass
>>> d = Dummy()
>>> p = ObjectProxy(d)
>>> p.foo = "bar"
>>> d.foo
'bar'
>>> del p.foo
>>> hasattr(d,'foo')
False
< H2>回调代理< /H2>有时,您可能希望在任何时候动态地确定代理的主题
使用代理。为此,可以使用CallbackProxy
类型,
它接受回调函数并创建将调用
回调以获取目标。下面是一个计数器的快速示例
每次使用都会递增,从0到3::
>>> from proxytypes3 import CallbackProxy
>>> ct = -1
>>> def callback():
... global ct
... if ct == 3: raise StopIteration
... ct += 1
... return ct
>>> counter = CallbackProxy(callback)
>>> counter
0
>>> counter
1
>>> str(counter)
'2'
>>> hex(counter)
'0x3'
>>> counter
Traceback (most recent call last):
...
StopIteration
如您所见,在尝试使用
代理。这是一个有点傻的例子;一个更好的例子是
始终等于其线程id的thread_id
代理
跑进去。
可以通过get_callback
获取或更改回调代理的回调。
以及set_callback
函数:
>>> from proxytypes3 import get_callback, set_callback
>>> set_callback(counter, lambda: 42)
>>> counter
42
>>> get_callback(counter)
<function <lambda> at ...>
< H2>懒惰代理< /H2>LazyProxy
与CallbackProxy
类似,但它的回调被调用
最多一次,然后缓存::
>>> from proxytypes3 import LazyProxy
>>> def callback():
... print("called!")
... return 42
>>> lazy = LazyProxy(callback)
>>> lazy
called!
42
>>> lazy
42
可以在lazy上使用get_callback
和set_callback
函数
代理,但如果调用已经被调用::
>>> set_callback(lazy, lambda: 99)
>>> lazy
42
但是您可以使用get_cache
和set_cache
函数来篡改
缓存值:
>>> from proxytypes3 import get_cache, set_cache
>>> get_cache(lazy)
42
>>> set_cache(lazy, 99)
>>> lazy
99
包装纸
ObjectWrapper
、CallbackWrapper
和LazyWrapper
类是
类似于他们的代理副本,除了他们打算
子类,以便添加自定义的额外属性或方法。任何属性
存在于这些类的子类中的将从
包装器实例,而不是包装对象。例如:
>>> from proxytypes3 import ObjectWrapper
>>> class NameWrapper(ObjectWrapper):
... name = None
... def __init__(self, ob, name):
... ObjectWrapper.__init__(self, ob)
... self.name = name
... def __str__(self):
... return self.name
>>> w = NameWrapper(42, "The Ultimate Answer")
>>> w
42
>>> print(w)
The Ultimate Answer
>>> w * 2
84
>>> w.name
'The Ultimate Answer'
注意,您添加的任何属性都必须在类中定义。你不能 在运行时添加任意属性,因为它们将在包装的 对象而不是包装器:
>>> w.foo = 'bar'
Traceback (most recent call last):
...
AttributeError: 'int' object has no attribute 'foo'
注意,这意味着所有实例属性都必须实现为
插槽、属性或在类主体中定义了默认值(如
name = None
如上图所示)。
CallbackWrapper
和LazyWrapper
基类基本相同
作为ObjectWrapper
,除了它们使用回调或缓存的延迟回调
而不是期待一个对象作为他们的主题。
除了上面描述的所有具体类之外,还有两个
抽象基类:AbstractProxy
和AbstractWrapper
。如果你愿意
要创建一个可以与任何具体类型一起使用的MIXIN类型
应该将抽象版本子类化并将__slots__
设置为空列表:
>>> from proxytypes3 import AbstractWrapper
>>> class NamedMixin(AbstractWrapper):
... __slots__ = []
... name = None
... def __init__(self, ob, name):
... super(NamedMixin, self).__init__(ob)
... self.name = name
... def __str__(self):
... return self.name
然后,当您将其与相应的基类混合时,可以将其添加回
任何必要的时隙,或者省略__slots__
以给出子类实例
他们自己的字典:
>>> from proxytypes3 import CallbackWrapper, LazyWrapper
>>> class NamedObject(NamedMixin, ObjectWrapper): pass
>>> class NamedCallback(NamedMixin, CallbackWrapper): pass
>>> class NamedLazy(NamedMixin, LazyWrapper): pass
>>> print(NamedObject(42, "The Answer"))
The Answer
>>> n = NamedCallback(callback, "Test")
>>> n
called!
42
>>> n
called!
42
>>> n = NamedLazy(callback, "Once")
>>> n
called!
42
>>> n
42
AbstractProxy
和AbstractWrapper
基类都由
假定^ {CD3}}将是被包装或代理的对象。如果
你不想用标准的三种方式来定义
__subject__
(即作为对象、回调或延迟回调),您将需要
子类AbstractProxy
或AbstractWrapper
并提供您自己的
定义__subject__
的方法。
测试
要跨多个python版本运行unittests,首先安装必要的python版本。在ubuntu上,运行:
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get update
sudo apt-get install python-dev python3.4-minimal python3.4-dev python3.5-minimal python3.5-dev python3.6 python3.6-dev
然后运行所有tests:
tox
为特定环境(如Python2.7)运行测试:
tox -e py27
运行特定测试:
export TESTNAME=.additional_tests; tox -e py34
export TESTNAME=.TestObjectProxy.testNumbers; tox -e py36