我正在寻找方法将方法调用从对象(包装器)传递到对象的成员变量(wrappee).可能有许多方法需要外部化,因此在向wrapee添加方法时,如果不更改包装器的接口,这样做是有帮助的.
class Wrapper(object)
def __init__(self, wrappee):
self.wrappee = wrappee
def foo(self):
return 42
class Wrappee(object):
def bar(self):
return 12
o2 = Wrappee()
o1 = Wrapper(o2)
o1.foo() # -> 42
o1.bar() # -> 12
o1.<any new function in Wrappee>() # call directed to this new function
Run Code Online (Sandbox Code Playgroud)
如果这个调用重定向是"快速"(相对于直接调用,即不增加太多开销),那将是很好的.
Raf*_*ros 12
一个稍微优雅的解决方案是在包装类上创建一个"属性代理":
class Wrapper(object):
def __init__(self, wrappee):
self.wrappee = wrappee
def foo(self):
print 'foo'
def __getattr__(self, attr):
return getattr(self.wrappee, attr)
class Wrappee(object):
def bar(self):
print 'bar'
o2 = Wrappee()
o1 = Wrapper(o2)
o1.foo()
o1.bar()
Run Code Online (Sandbox Code Playgroud)
所有的魔法都发生在类的__getattr__方法上Wrapper,它将尝试访问Wrapper实例上的方法或属性,如果它不存在,它将尝试包装的方法或属性.
如果您尝试访问任一类上不存在的属性,您将得到:
o2.not_valid
Traceback (most recent call last):
File "so.py", line 26, in <module>
o2.not_valid
File "so.py", line 15, in __getattr__
raise e
AttributeError: 'Wrappee' object has no attribute 'not_valid'
Run Code Online (Sandbox Code Playgroud)
aba*_*ert 10
如果你真的需要快速,那么最快的选择就是在初始化时自行编写代码:
def __init__(self, wrappee):
for name, value in inspect.getmembers(wrappee, callable):
if not hasattr(self, name):
setattr(self, name, value)
Run Code Online (Sandbox Code Playgroud)
这将为您的Wrapper实例提供正常的数据属性,其值是绑定的方法Wrappee.那应该是非常快.是吗?
class WrapperA(object):
def __init__(self, wrappee):
self.wrappee = wrappee
for name, value in inspect.getmembers(wrappee, callable):
if not hasattr(self, name):
setattr(self, name, value)
class WrapperB(object):
def __init__(self, wrappee):
self.wrappee = wrappee
def __getattr__(self, name):
return getattr(self.wrappee, name)
In [1]: %run wrapper
In [2]: o2 = Wrappee()
In [3]: o1a = WrapperA(o2)
In [4]: o1b = WrapperB(o2)
In [5]: %timeit o2.bar()
10000000 loops, best of 3: 154 ns per loop
In [6]: %timeit o1a.bar()
10000000 loops, best of 3: 159 ns per loop
In [7]: %timeit o1b.bar()
1000000 loops, best of 3: 879 ns per loop
In [8]: %timeit o1b.wrapper.bar()
1000000 loops, best of 3: 220 ns per loop
Run Code Online (Sandbox Code Playgroud)
因此,复制绑定方法的成本为3%(不确定为什么它甚至有那么多......).任何比这更动态的东西都必须从中拉出属性self.wrapper,其开销至少为66%.通常的__getattr__解决方案有471%的开销(并且添加不必要的额外内容只会使它变慢).
所以,对于绑定方法黑客而言,这听起来像一个开放和关闭的胜利,对吗?
不必要.471%的开销仍然只有700纳秒.这真的会对你的代码产生影响吗?可能不会,除非它在紧密循环中使用 - 在这种情况下,你几乎肯定会想要将方法复制到局部变量.
这个黑客有很多缺点.这不是"明显的做法".它不适用于未在实例dict上查找的特殊方法.它静态地删除了属性o2,因此如果您稍后创建任何新属性,o1将不会代理它们(尝试以这种方式构建动态代理链...).如果你有很多代理,那就浪费了很多内存.它在Python 2.x和3.x之间略有不同(如果你依赖的话,甚至在2.x和3.x系列中也是如此inspect),而__getattr__从2.3到现在(以及在交替中)非常谨慎地保持相同Python实现也是如此).等等.
如果你真的需要速度,你可能想要考虑混合:一种__getattr__缓存代理方法的方法.您甚至可以分两个阶段完成:一次调用一次,将未绑定的方法缓存在一个类属性中并在运行中绑定它; 如果然后重复调用,则将绑定方法缓存在实例属性中.
| 归档时间: |
|
| 查看次数: |
5543 次 |
| 最近记录: |