Python父类“包装”子类方法
我在我的Python代码中遇到了以下情况:
class Parent(object):
def run(self):
print "preparing for run"
self.runImpl()
print "run done"
class Child(Parent):
def runImpl(self):
print "child running"
不过,我有一些情况涉及多个“装饰器”,它们在执行‘runImpl’之前和之后会进行不同的设置和清理。目前,我不得不在我的类Parent
、Child
和ChildSingleProcess
中定义run()
、runImpl()
和runImplSingleProcess()
。
我在寻找一种这样的解决方案:
class Parent(object):
@wrapping_child_call
def run(self, func_impl, *args, **kwargs)
print "preparing for run"
func_impl(*args, **kwargs)
print "run done"
class Child(Parent):
def run(self):
print "child running"
这样一来,Child
类几乎不需要知道这些事情的发生。
另外,可能还会有多个继承的问题。如果一个Child
同时继承自Parent1
和Parent2
,我老实说不知道应该是什么样的正确行为。
有没有人知道一个好的、自然的方式来实现这个?还是说我在设计上搞错了?
谢谢
Yonatan
4 个回答
你可以得到这个:
class Parent(object):
def run(self, func_impl, *args, **kwargs):
print "preparing for run"
func_impl(*args, **kwargs)
print "run done"
class Child(Parent):
@wrapped_in_parent_call
def run(self):
print "child running"
使用:
import functools
class wrapped_in_parent_call(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, type=None):
@functools.wraps(self.func)
def wrapped(*args, **kwargs):
owning_class = self.func.__get__(obj, type).im_class
parent_func = getattr(super(owning_class, obj), self.func.__name__)
return parent_func(
lambda *a, **kw: self.func(obj, *a, **kw),
*args,
**kwargs
)
return wrapped
(仅适用于Python 2)
Yonatan,你的问题不太清楚!根据不同的情况,你可以使用很多种不同的设计方案。
一种解决方案是设置明确的setup()和teardown()方法,这些方法会在run()方法中被调用,然后再调用runImpl()。这样,子类就可以根据需要来包装或重写这些方法。
class Runner(object):
def run(self):
self.setup()
self.runImpl()
self.teardown()
def setup(self):
pass
def teardown(self):
pass
class RunnerImplementation(Runner):
def runImpl(self):
pass # do some stuff
def setup(self):
print "doing setup"
super(RunnerImplementation, self).setup()
def teardown(self):
print "doing teardown"
super(RunnerImplementation, self).teardown()
不过,你提到了多重继承,这意味着你可能不应该朝这个方向去做。
你提到多重继承和包装(就像“装饰器”那样),让我猜测你想要能够编写不同的“运行器”实现,每个实现都有自己的setup和teardown过程,同时又能在不同的“运行器”之间重用一些setup和teardown的部分。
如果是这样的话,你可以定义一些资源,这些资源知道如何进行setup和teardown,然后每个运行器声明它需要哪些资源。run()方法会运行每个资源相关的setup和teardown代码,并将它们提供给runImpl()方法。
class Resource(object):
name = None # must give a name!
def setup(self):
pass
def teardown(self):
pass
class DatabaseResource(Resource):
name = "DB"
def setup(self):
self.db = createDatabaseConnection()
def teardown(self):
self.db.close()
class TracingResource(Resource):
name = "tracing"
def setup(self):
print "doing setup"
def teardown(self):
print "doing teardown"
class Runner(object):
RESOURCES = []
def run(self):
resources = {}
for resource_class in self.RESOURCES:
resource = resource_class()
resource.setup()
resources[resource_class.name] = resource
self.runImpl(resources)
# teardown in opposite order of setup
for resource in reversed(resources):
resource.teardown()
class RunnerA(Runner):
RESOURCES = [TracingResource, DatabaseResource]
def runImpl(self, resources):
resources['DB'].execute(...)
这里不要使用继承
换个思路来设计。与其用父子类的方式(这是一种“是一个”的关系),不如使用组合的方式,这样就变成了“拥有一个”的关系。你可以定义一些类来实现你想要的方法,而之前的父类可以用这些具体实现的类来创建实例。
class MyClass:
def __init__(self, impl)
self.impl = impl
def run(self,var):
print "prepare"
impl.runImpl(var)
print "I'm done"
class AnImplementation:
def runImpl(self,var):