跳转到主要内容

通用代理和包装类型

项目描述

objproxies模块提供了一些有用的基类,用于创建普通Python对象的代理和包装。代理对象自动将所有属性访问和操作委托给被代理对象。包装器类似,但可以被子类化以允许向包装对象添加额外的属性和操作。

请注意,这些代理类型并不旨在防篡改;可以使用代理的__subject__属性轻松访问对象的未代理形式,一些代理类型甚至允许设置此属性(这对于需要能够分发“前向引用”代理的懒惰创建循环结构的算法来说可能很有用。)

开发状态

这是Python 3的ProxyTypes,由Phillip J. Eby编写,作为PEAK的一部分,用于Python 2。

命名空间已从peak.util.proxies更改为objproxies。除此之外,它应该是一个兼容的替代品。

到目前为止已经完成了以下工作

  • 简化了文件和设置

  • 移植了unittest和doctests

  • 清理了语法

v1.0 TODO

  • 将模块转换为包,将不同功能分离到不同的模块中

  • 尽可能简化代码

  • 收到几位用户的积极反馈

欢迎贡献和错误报告。

测试

当nose可用时,可以使用以下方式运行所有测试

nosetests3 --with-doctest  --doctest-extension=rst .

否则,标准的Python即可满足需求

python -m unittest objproxies_tests.py
python -m doctest README.rst

代理基础

以下是对ObjectProxy类型的快速演示

>>> from objproxies import ObjectProxy
>>> p = ObjectProxy(42)

>>> p
42

>>> isinstance(p, int)
True

>>> p.__class__
<class 'int'>

>>> p*2
84

>>> 'X' * p
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

>>> hex(p)
'0x2a'

>>> chr(p)
'*'

>>> p ^ 1
43

>>> p ** 2
1764

如你所见,代理在虚拟上与它所代理的对象无法区分,除非通过其__subject__属性和其type()

>>> p.__subject__
42

>>> type(p)
<class 'objproxies.ObjectProxy'>

你可以改变ObjectProxy__subject__,然后它将指向其他对象

>>> p.__subject__ = 99
>>> p
99
>>> p-33
66

>>> p.__subject__ = "foo"
>>> p
'foo'

所有操作都委托给主体,包括setattrdelattr

>>> class Dummy: pass
>>> d = Dummy()
>>> p = ObjectProxy(d)

>>> p.foo = "bar"
>>> d.foo
'bar'

>>> del p.foo
>>> hasattr(d,'foo')
False

回调代理

有时,你可能希望在代理使用时动态确定代理的主题。为此,你可以使用CallbackProxy类型,它接受一个回调函数并创建一个将调用回调以获取目标的代理。以下是一个简单的例子,计数器在每次使用时都会递增,从0到3

>>> from objproxies import CallbackProxy

>>> callback = iter(range(4)).__next__
>>> counter = CallbackProxy(callback)

>>> counter
0
>>> counter
1
>>> str(counter)
'2'
>>> hex(counter)
'0x3'

>>> counter
Traceback (most recent call last):
  ...
StopIteration

如你所见,在尝试使用代理时,回调会自动调用。这是一个有些愚蠢的例子;更好的例子可能是一个thread_id代理,它始终等于它所运行的线程的ID。

可以通过get_callbackset_callback函数获取或更改回调代理的回调

>>> from objproxies import get_callback, set_callback
>>> set_callback(counter, lambda: 42)

>>> counter
42

>>> type(get_callback(counter))
<class 'function'>

懒惰代理

LazyProxyCallbackProxy类似,但它的回调最多调用一次,然后缓存

>>> from objproxies import LazyProxy

>>> def callback():
...     print("called")
...     return 42

>>> lazy = LazyProxy(callback)
>>> lazy
called
42
>>> lazy
42

你可以在懒代理上使用get_callbackset_callback函数,但如果回调已经被调用,则没有效果

>>> set_callback(lazy, lambda: 99)
>>> lazy
42

但你可以使用get_cacheset_cache函数来篡改缓存的值

>>> from objproxies import get_cache, set_cache
>>> get_cache(lazy)
42
>>> set_cache(lazy, 99)
>>> lazy
99

包装器

ObjectWrapperCallbackWrapperLazyWrapper类与它们的代理对应物类似,但它们旨在被继承以添加自定义额外属性或方法。这些类中的任何子类都将从包装实例而不是包装对象中读取或写入属性。例如

>>> from objproxies 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)。

CallbackWrapperLazyWrapper基类基本上与ObjectWrapper相同,不同之处在于它们使用回调或缓存的懒回调而不是期望主体是对象。

LazyWrapper对象在处理昂贵的资源时特别有用,如连接或网络浏览器,以避免在绝对需要之前创建它们。

然而,通常在使用后必须通过调用某种类型的“close”方法来释放资源。在这种情况下,可以在调用close本身时触发延迟创建,因为此时对象不再需要。因此,当扩展LazyWrapper时,可以使用@lazymethod替换来覆盖这些方法。

>>> from objproxies import LazyWrapper, lazymethod

>>> class LazyCloseable(LazyWrapper):
...     @lazymethod
...     def tell(self):
...         return 0
...     @lazymethod
...     def close(self):
...         print("bye")
...     @lazymethod
...     def __bool__(self):
...         return False

>>> import tempfile

>>> def openf():
...     print("called")
...     return tempfile.TemporaryFile('w')

>>> lazyfile = LazyCloseable(openf)
>>> lazyfile.tell()
0
>>> lazyfile.close()
bye
>>> bool(lazyfile)
False

>>> lazyfile = LazyCloseable(openf)
>>> lazyfile.write('wake up')
called
7
>>> lazyfile.tell()
7
>>> lazyfile.close()  # close for real
>>> bool(lazyfile)
True

高级:自定义子类和混入

除了上面描述的所有具体类之外,还有两个抽象基类:AbstractProxyAbstractWrapper。如果您想创建一种可以与任何具体类型一起使用的mixin类型,您应该从抽象版本派生,并将__slots__设置为空列表。

>>> from objproxies 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 objproxies 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

AbstractProxyAbstractWrapper基类都通过假设self.__subject__将是包装或代理的对象来工作。如果您不想使用定义__subject__的任何三种标准方式(即作为对象、回调或延迟回调),则需要从AbstractProxyAbstractWrapper派生,并提供自己的定义__subject__的方式。

项目详情


下载文件

下载适合您平台的应用程序。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分发

objproxies-0.9.4.tar.gz (7.2 kB 查看哈希值)

上传时间

支持者