我目前正在研究一种可维护且易于使用的方法来创建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
这使得代码相当短,修改A1、A2等。。。类不需要对补丁进行任何更改,这非常好。然而,我显然担心在多个类之间共享__dict__
的影响。我的问题是:
__dict__
时,我应该注意哪些陷阱?你知道吗__dict__
和__class__
来创建对象的方法?你知道吗__dict__
,我被迫将它添加到类本身,或者作为类成员,或者作为“捕获的”变量。我还能把它放在别的位置吗?理想情况下,我希望避免为name的每个实例创建一个新类(我只想为每个原始类动态创建一个新类)其他考虑过的解决方案:
代理对象(请参见胡安帕.阿里维拉加答案)是一个近乎完美的解决方案,但当修补的函数被另一个函数内部调用时,它就不够了。具体来说,在上面发布的代码中,对meeting
函数的最终调用将使用原始实现,而不是修补的实现。参见https://repl.it/repls/OrneryLongField以获取示例。你知道吗
在我看来,您似乎想要一个代理对象,这通常就是视图。简而言之,模式可以很简单(对于只读代理),如下所示:
__getattr__
的好处是,只有在找不到属性时才调用它。如果您想要写访问,那么您需要更加小心,实现__setattribute__
,这是总是调用的,并且很容易在不经意间触发无限递归。你知道吗注意,因为我们在被代理的对象上使用
getattr
,所以我们不必管理重新创建接口!方法解析、描述符协议(soproperty
)、继承等都由常规机制处理:相关问题 更多 >
编程相关推荐