显而易见的事情是将您的“引擎”作为每个“元素”对象中的属性(或在其中调用渲染方法时作为值传递) - 为此,您的“状态”对象必须知道Engine 实例,并在实例化时将其传递给每个 Element,或者在每个渲染方法调用时传递它——这似乎让您感到困扰。
同样,“状态”要知道“引擎”,它必须知道它 - 无论是在创建时,还是在调用将触发渲染(“状态”)的操作时。
Python 的动态特性确实有办法让你不必每次都显式地编写所有这些 - 是的 - 但你必须小心不要把它埋得太多,这样你的代码就无法维护了。让我们看看我是否可以在这里归结为一些例子:
class WinMain(object):
def __init__(self):
self.engine = Engine()
def loop_once(self):
self.engine.step()
class Engine(object):
def __init__(self):
self.state = State()
def step(self):
self.state.update(self)
def render(self, obj):
print (obj)
class State(object):
def __init__(self):
self.element = Element()
def update(self, engine):
self.element.render(engine)
class Element(object):
def render(self, engine):
engine.render(self)
if __name__ == "__main__":
win = WinMain()
win.loop_once()
此代码仅在更新时传递 Engine 对象——这比将引擎固定为状态属性或元素属性更有趣,因为它允许 Element 实例完全属于不同的引擎。它还将“引擎”参数与元素创建时间分离 - 您抱怨您根本不想使用引擎的部分 - 所以它可以不那么混乱。
无论哪种方式,您都必须让“元素”以某种方式了解它们的“对象”——如果您不明确传递参数,则必须隐式地进行(即让“引擎”实例在“状态”中已知)和“元素”以某种方式)。一种方法是编写基类或元类以在实例化时执行帧自省,并将调用上下文中的“self”变量绑定到新对象中的属性。这种隐含的做法闻起来很糟糕。:-) 另一种方法是将对象的关系保存在单独的数据结构中,并使用您的方法可以导航的 DOM 接口。但是,无论如何,您都必须进行额外的调用来注释关系(也许额外的调用可以进行自省的基类或元类,如上所示)。
再想一想——Python 应用服务器“zope”使用“acquisition”解决了这个问题:zope 应用程序中的所有对象都位于树中——当人们试图从没有它的对象中检索属性时(例如,在尝试调用元素的“render”方法时),获取机制在树上“向上”搜索该属性,直到找到支持该方法的父级。然后调用它,并获取原始对象作为参数。这对您来说是一个可能的解决方案,但不仅需要实现获取机制的基类,
这里有一个简单的“收购”实现来简化你的案例:
class WinMain(object):
def __init__(self):
self.struct = []
self.engine = Engine()
def loop_once(self):
self.engine.step()
class TraverseError(AttributeError):
pass
class AquisitionBase(object):
tree = {}
def __getattr__(self, attr):
# __getattr__ is only invoked when the original object does not have
# attr by itself
try:
return getattr(self.__class__.tree[id(self)], attr)
except (IndexError, AttributeError), error:
raise TraverseError(error)
def __setattr__(self, attr, obj):
cls = self.__class__
# Anotate parent:
cls.tree[id(obj)] = self
return super(AquisitionBase,self).__setattr__(attr, obj)
class Engine(AquisitionBase):
def __init__(self):
self.state = State()
def step(self):
self.state.update()
def render(self, obj):
print (obj)
class State(AquisitionBase):
def __init__(self):
self.element = Element()
def update(self):
self.element.render(self.element)
class Element(AquisitionBase):
pass
if __name__ == "__main__":
win = WinMain()
win.loop_once()
请注意,如果您有包含“元素”的列表或字典,则可以通过创建知道此方案的列表和字典的子类来克服有关容器的问题,并基本上实现一个__setitem__
注释父级的方法对象也被归于那里。
本质上——不管怎样,考虑一下,传递“引擎”参数听起来更好——即使 Python 可以满足你的要求。