我目前正在寻找一种在python对象上创建“视图”的可维护且易于使用的方法。具体来说,我有很多类,它们共享几种常见的方法,并且我想包装这些类的实例,以修改这些方法的行为。
当然,我可以按照包装模式为每个类创建一个新类,重新定义完整的接口并将每个方法重定向到原始对象,但我希望覆盖的对象除外。这是不切实际的,因为所需的代码量很大,并且在更改任何类时都需要维护。
一些实验表明,我可以大量使用元类和自省功能来生成包装器,以“重新创建”包装器对象中的接口,但是事实证明,使用和调试它非常可怕,特别是如果A具有属性(不包括代码)
第二次尝试表明,通过共享__dict__属性和覆盖,可以用相当少的代码来完成__class__。这将导致以下代码(https://repl.it/repls/InfantileAshamedProjector):
##############################
# Existing code
##############################
class A1:
def __init__(self, eggs):
self.eggs = eggs
# Lots of complicated functions and members
def hello(self):
print ("hello, you have %d eggs" % self.eggs)
def meeting(self):
self.hello()
print ("goodbye")
# Lots of complicated functions calling hello.
# Lots of A2, A3, A4 with the same pattern
##############################
# "Magic" code for view generation
##############################
class FutureView:
pass
def create_view(obj, name):
class View(obj.__class__):
def hello(self):
print ("hello %s, you have %d eggs" % (name, self.eggs))
view = FutureView()
view.__dict__ = obj.__dict__
view.__class__ = View
return view
##############################
# Sample of use
##############################
a = A1(3)
a.hello() # Prints hello, you have 3 eggs
v = create_view(a, "Bob")
v.hello() # Prints hello Bob, you have 3 eggs
a.eggs = 5
a.hello() # Prints hello, you have 5 eggs
v.hello() # Prints hello Bob, you have 5 eggs
a.meeting() # Prints hello, you have 5 eggs. Goodbye
v.meeting() # Prints hello Bob, you have 5 eggs. Goodbye
Run Code Online (Sandbox Code Playgroud)
这使得代码相当短,并且修改A1,A2等类不需要对补丁进行任何更改,这非常好。但是,我显然担心__dict__多个类之间共享的含义。我的问题是:
__dict__?__dict__和the ,创建对象的最佳方式(减少不良程度)是__class__什么?__dict__,所以我被迫将其作为类成员或“捕获”变量添加到类本身。我还有其他位置吗?理想情况下,我希望避免为每个名称实例创建一个新类(我只想为每个原始类动态创建一个新类)其他考虑的解决方案:
代理对象(请参阅juanpa.arrivillaga答案)几乎是一种完美的解决方案,但是当修补函数被另一个函数在内部调用时,它就不够用了。具体来说,在上面发布的代码中,对该meeting函数的最终调用将使用原始实现而不是已修补的实现。有关示例,请参见https://repl.it/repls/OrneryLongField。
在我看来,您想要一个代理对象,这就是视图通常的样子。简而言之,该模式可以像这样简单(对于只读代理):
class View:
def __init__(self, obj):
self._obj = obj
def __getattr__(self, attr):
return getattr(self._obj, attr)
Run Code Online (Sandbox Code Playgroud)
好处__getattr__是它仅在未找到属性时调用。如果你想要写访问,那么你需要更加小心,并实现__setattribute__总是被调用的,并且很容易无意中触发无限递归。
请注意,因为我们正在被代理的对象上使用getattr,所以我们不必管理重新创建接口!方法解析、描述符协议(so property)、继承等都由通常的机制处理:
In [1]: class View:
...: def __init__(self, obj):
...: self._obj = obj
...: def __getattr__(self, attr):
...: return getattr(self._obj, attr)
...:
In [2]: class UrFoo:
...: def __init__(self, value):
...: self.value = value
...: def foo(self):
...: return self.value
...:
In [3]: class Foo(UrFoo):
...: def frognicate(self):
...: return self.value * 42
...: @property
...: def baz(self):
...: return 0
...:
In [4]: foo = Foo(8)
In [5]: view = View(foo)
In [6]: view.foo()
Out[6]: 8
In [7]: view.frognicate()
Out[7]: 336
In [8]: view.baz
Out[8]: 0
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
60 次 |
| 最近记录: |