使用一个方法中的docstring自动覆盖另一个方法的docstring

时间:2008-09-16 12:46:28

标签: python metaclass

问题:我有一个包含模板方法execute的类,它调用另一个方法_execute。子类应该覆盖_execute以实现某些特定功能。此功能应记录在_execute的文档字符串中。 高级用户可以创建自己的子类来扩展库。但是,处理此类子类的另一个用户应该只使用execute,因此如果他使用help(execute),他将看不到正确的文档字符串。

因此,以这样的方式修改基类会很好:在子类中execute的文档字符串会自动替换为_execute的文档字符串。任何想法如何做到这一点?

我正在考虑使用元类来实现这一点,以使用户完全透明。

5 个答案:

答案 0 :(得分:4)

好吧,如果您不介意在子类中复制原始方法,可以使用以下技术。

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

答案 1 :(得分:2)

是否有理由不能直接覆盖基类的execute函数?

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副本:

class Derived(Base):
    def execute(self):
        return Base.execute(self)

    class _execute(self):
        """Docstring for subclass"""
        ...

    execute.__doc__= _execute.__doc__

但这类似于重新定义execute ...

的迂回方式

答案 2 :(得分:1)

看看functools.wraps()装饰器;它完成了所有这一切,但我不知道你是否可以让它在正确的背景下运行

答案 3 :(得分:0)

文档字符串存储在__doc__中,因此在事后事件基于_execute的文档字符串重新分配文档字符串并不会太难。

基本上:

class MyClass(object):
    def execute(self):
        '''original doc-string'''
        self._execute()

class SubClass(MyClass):
    def _execute(self):
        '''sub-class doc-string'''
        pass

    # re-assign doc-string of execute
    def execute(self,*args,**kw):
        return MyClass.execute(*args,**kw)
    execute.__doc__=_execute.__doc__

必须重新声明Execute,即doc字符串附加到SubClass的执行版本而不是MyClass(否则会干扰其他子类)。< / p>

这不是一种非常整洁的方式,但是从库的用户的POV中它应该给出期望的结果。然后,您可以将其包装在元类中,以便更轻松地进行子类化。

答案 4 :(得分:0)

我同意最简单,最Pythonic的方法是简单地重新定义子类中的execute并让它调用基类的execute方法:

class Sub(Base):
    def execute(self):
        """New docstring goes here"""
        return Base.execute(self)

这是完成你想要的代码的很少的代码;唯一的缺点是你必须在扩展Base的每个子类中重复这段代码。但是,这对于您想要的行为来说是一个很小的代价。

如果你想要一种草率和冗长的方法来确保动态生成执行的文档字符串,你可以使用描述符协议,这将比这里的其他提议少得多。这很烦人,因为您不能只在现有函数上设置描述符,这意味着必须使用__call__方法将execute写为单独的类。

这是执行此操作的代码,但请记住,上面的示例更简单,更Pythonic:

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!"