支持稍加修改的对象重用
dm.reuse的Python项目详细描述
在新上下文中重用(稍微修改)对象的实用程序。
目前,有两个实用程序:rebindFunction和 OverridingProxy。
rebindFunction
rebindFunction允许在更改时重用函数的代码 名称、全局参数、默认参数、属性和/或使用的名称。
让我们看一个小例子。函数f访问全局变量 i和j。
示例
>>> i = 1; j = 2 >>> def f(): return i, j ... >>> f() (1, 2)
我们想得到一个新的函数g,它将i绑定到-1:
>>> from dm.reuse import rebindFunction >>> g=rebindFunction(f, i=-1) >>> g() (-1, 2)
我们不仅可以通过关键字参数,还可以通过 还有一本字典:
>>> g=rebindFunction(f, dict(i=-1, j=-2)) >>> g() (-1, -2)
通常,函数名是从原来的函数取而代之的, 但可以更改:
>>> f.__name__ 'f' >>> g.__name__ 'f' >>> g=rebindFunction(f, dict(i=-1, j=-2), funcName='g') >>> g.__name__ 'g' >>> g() (-1, -2)
原始函数docstring也被接管,除非 覆盖:
>>> f.func_doc = 'some documentation' >>> g=rebindFunction(f, dict(i=-1, j=-2)) >>> f.__doc__ is g.__doc__ True >>> g=rebindFunction(f, dict(i=-1, j=-2), funcDoc='some new documentation') >>> g.__doc__ 'some new documentation'
可以添加、删除或更改参数的默认值。 可识别未知参数:
>>> def f(a1, a2=2): return a1, a2 ... >>> g=rebindFunction(f, argRebindDir=dict(a1=1)) >>> g() (1, 2)
>>> from dm.reuse import REQUIRED >>> g=rebindFunction(f, argRebindDir=dict(a2=REQUIRED)) >>> g(1) #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: f() takes exactly 2 arguments (1 given)
>>> g=rebindFunction(f, argRebindDir=dict(a2=10)) >>> g(1) (1, 10)
>>> g=rebindFunction(f, argRebindDir=dict(a3=10)) Traceback (most recent call last): ... ValueError: unknown arguments in `argRebindDir`: a3
最后,函数属性可以通过proprebinddir反弹。 我们很小心,给新函数一个单独的新属性dict。
>>> f.prop='p' >>> g=rebindFunction(f) >>> g.prop 'p' >>> g=rebindFunction(f, propRebindDir=dict(prop='P', prop2='p2')) >>> g.prop, g.prop2 ('P', 'p2') >>> f.__dict__ {'prop': 'p'}
有时,函数使用的本地导入不充分 在新的背景下。为了控制他们,名字 在函数代码内部使用可以更改。
>>> def f(a): import codecs; return codecs, a ... >>> g=rebindFunction(f, nameRebindDir=dict(codecs='urllib')) >>> r = g(1) >>> r[0].__name__, r[1] ('urllib', 1)
这样,对全局变量的引用也可以更改。
>>> i1, i2 = 1, 2 >>> def f(): return i1 ... >>> g=rebindFunction(f, nameRebindDir=dict(i1='i2')) >>> g() 2
OverridingProxy
有时,您必须使用一个对象,该对象的总体功能 基本上是足够的,但小的方面需要改变。 在这些情况下,OverridingProxy可能会有帮助。它允许 代理对象。代理主要表现为代理 对象,但某些属性/方法被重写。
示例
我们设置了一个play对象c,并假装必须使用它 使用修改后的属性attr1。我们通过代理来实现这一点 它的行为类似于c,但属性已更改。
>>> from dm.reuse.proxy import OverridingProxy, make_proxy >>> >>> class C(object): ... attr1 = 1 ... attr2 = 2 ... def f(self): return self.attr1, self.attr2 ... def g(self): return self.f() ... >>> c = C() >>> p = make_proxy(c, attr1='overridden attr1') >>> p.__class__ is C True >>> isinstance(p, C) True >>> p.attr1 'overridden attr1'
当我们调用使用 过度驾驶属性。
>>> p.f() ('overridden attr1', 2)
如果我们为代理属性分配一个新值,那么 代理对象上的相应属性被更改。 因此,更改重写的属性 显然没有效果-这不直观,可能会改变 未来。
>>> p.attr2 = "new attr2" >>> p.f() ('overridden attr1', 'new attr2') >>> p.attr1 = "new attr1" >>> c.attr1 'new attr1' >>> p.f() ('overridden attr1', 'new attr2')
您可以使用方法set_proxy_attribute来更改 代理的属性而不是代理方法的属性。
>>> p.set_proxy_attribute("attr1", "new overridden attr1") >>> p.f() ('new overridden attr1', 'new attr2')
一个代理比较通常等于它的代理对象。有时候, 这也适用于相反的情况。但是,两个 对于同一代理对象的代理比较通常是相等的。
>>> p == c True >>> c == p True >>> make_proxy(c) == p True
通常,您不会重写简单的属性,而是重写方法。 使用自定义代理类更容易实现这一点。 在许多情况下,重写方法将要调用 重写的方法;这可以通过call_proxied_method实现。
>>> class MyProxy(OverridingProxy): ... def g(self): ... print ("g called") ... return self.call_proxied_method("g") ... >>> p = make_proxy(c, MyProxy, attr1="overridden attr1") >>> p.g() g called ('overridden attr1', 'new attr2')P>代理对特殊方法的支持有限 从而支持例如订阅。注意这个支持 是不完整的,可能会有惊喜。
>>> class MyDict(dict, C): pass ... >>> md = MyDict(dict(a=1, b=2)) >>> p = make_proxy(md, attr1="overridden attr1") >>> p.f() ('overridden attr1', 2) >>> p["a"] 1 >>> class MySequence(C): ... seq = 0, 1, 2 ... def __getitem__(self, i): return self.seq[i] ... >>> c = MySequence() >>> p = make_proxy(c, seq=(10, 11, 12)) >>> p[0] 10 >>> p = make_proxy(c) >>> p[0] 0
通常,描述符也会看到修改后的状态。
>>> class WithProperty(C): ... @property ... def attr1_prop(self): return self.attr1 ... >>> c = WithProperty() >>> p = make_proxy(c, attr1="overridden attr1") >>> p.attr1_prop 'overridden attr1'
历史记录
- 2.1
- 添加了OverridingProxy,允许使用 小改动
- 2.0
添加了对python 3的部分支持(仅限关键字参数和 尚未支持批注)
在2.7之前放弃了对python的支持
- 1.1
- nameRebindDir支持已添加