问题:我有一个包含模板方法execute的类,该方法调用另一个方法_execute。 子类应该覆盖_execute以实现某些特定功能。 此功能应记录在_execute的文档字符串中。
高级用户可以创建自己的子类来扩展库。 但是,另一个处理此类子类的用户只能使用execute,因此如果使用help(execute),他将看不到正确的文档字符串。
因此,最好以这样的方式修改基类,以便在子类中将execute的文档字符串自动替换为_execute的文档字符串。 任何想法如何做到这一点?
我正在考虑使用元类来做到这一点,以使它对用户完全透明。
好吧,如果您不介意在子类中复制原始方法,则可以使用以下技术。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import new
def copyfunc(func):
return new.function(func.func_code, func.func_globals, func.func_name,
func.func_defaults, func.func_closure)
class Metaclass(type):
def __new__(meta, name, bases, attrs):
for key in attrs.keys():
if key[0] == '_':
skey = key[1:]
for base in bases:
original = getattr(base, skey, None)
if original is not None:
copy = copyfunc(original)
copy.__doc__ = attrs[key].__doc__
attrs[skey] = copy
break
return type.__new__(meta, name, bases, attrs)
class Class(object):
__metaclass__ = Metaclass
def execute(self):
'''original doc-string'''
return self._execute()
class Subclass(Class):
def _execute(self):
'''sub-class doc-string'''
pass |
您是否有原因不能直接覆盖基类的execute函数?
1 2 3 4 5 6 7 8 9
| class Base(object):
def execute(self):
...
class Derived(Base):
def execute(self):
"""Docstring for derived class"""
Base.execute(self)
...stuff specific to Derived... |
如果您不想执行上述操作:
方法对象不支持写入__doc__属性,因此必须在实际的函数对象中更改__doc__。由于您不想覆盖基类中的那个,因此必须为每个子类提供自己的execute副本:
1 2 3 4 5 6 7 8 9
| class Derived(Base):
def execute(self):
return Base.execute(self)
class _execute(self):
"""Docstring for subclass"""
...
execute.__doc__= _execute.__doc__ |
但这类似于重新定义execute的环形路...
看一下functools.wraps()装饰器;它可以完成所有这些工作,但是我不知道是否可以在正确的环境中运行它
我同意,最简单,最Python化的方法是在子类中重新定义execute,并使其调用基类的execute方法:
1 2 3 4
| class Sub(Base):
def execute(self):
"""New docstring goes here"""
return Base.execute(self) |
这是完成所需内容的代码很少。唯一的缺点是您必须在扩展Base的每个子类中重复此代码。但是,这对于您想要的行为来说是很小的代价。
如果您需要一种草率而冗长的方式来确保动态生成要执行的文档字符串,则可以使用描述符协议,该协议的代码量明显少于此处的其他建议。这很烦人,因为您不能只在现有函数上设置描述符,这意味着必须使用__call__方法将execute编写为单独的类。
这是执行此操作的代码,但请记住,我上面的示例更简单,更Pythonic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Executor(object):
__doc__ = property(lambda self: self.inst._execute.__doc__)
def __call__(self):
return self.inst._execute()
class Base(object):
execute = Executor()
class Sub(Base):
def __init__(self):
self.execute.inst = self
def _execute(self):
"""Actually does something!"""
return"Hello World!"
spam = Sub()
print spam.execute.__doc__ # prints"Actually does something!"
help(spam) # the execute method says"Actually does something!" |
那么文档字符串存储在__doc__中,因此事后根据_execute的文档字符串重新分配它并不难。
基本上:
必须重新声明Execute,以使doc字符串附加到SubClass而不是MyClass的execute版本上(否则会干扰其他子类)。
这不是一个很整洁的方法,但是从库用户的POV来看,它应该可以提供理想的结果。然后,您可以将其包装在元类中,以使子类化的人员更容易进行操作。