使用一种方法的文档字符串自动覆盖另一种方法的文档字符串

发布于 2024-07-06 08:49:58 字数 428 浏览 7 评论 0原文

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

因此,最好以这样的方式修改基类,以便在子类中 execute 的文档字符串自动替换为 _execute 的文档字符串。 有什么想法可以做到这一点吗?

我正在考虑使用元类来做到这一点,使这对用户完全透明。

The problem: I have a class which contains a template method execute which calls another method _execute. Subclasses are supposed to overwrite _execute to implement some specific functionality. This functionality should be documented in the docstring of _execute.
Advanced users can create their own subclasses to extend the library. However, another user dealing with such a subclass should only use execute, so he won't see the correct docstring if he uses help(execute).

Therefore it would be nice to modify the base class in such a way that in a subclass the docstring of execute is automatically replaced with that of _execute. Any ideas how this might be done?

I was thinking of metaclasses to do this, to make this completely transparent to the user.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

沫离伤花 2024-07-13 08:49:58

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

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

Well, if you don't mind copying the original method in the subclass, you can use the following technique.

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
平生欢 2024-07-13 08:49:58

是否有原因无法直接重写基类的 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 的一种迂回方式...

Is there a reason you can't override the base class's execute function directly?

class Base(object):
    def execute(self):
        ...

class Derived(Base):
    def execute(self):
        """Docstring for derived class"""
        Base.execute(self)
        ...stuff specific to Derived...

If you don't want to do the above:

Method objects don't support writing to the __doc__ attribute, so you have to change __doc__ in the actual function object. Since you don't want to override the one in the base class, you'd have to give each subclass its own copy of execute:

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

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

    execute.__doc__= _execute.__doc__

but this is similar to a roundabout way of redefining execute...

裸钻 2024-07-13 08:49:58

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

Look at the functools.wraps() decorator; it does all of this, but I don't know offhand if you can get it to run in the right context

掩饰不了的爱 2024-07-13 08:49:58

那么文档字符串存储在 __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__

必须重新声明执行,以便将文档字符串附加到 SubClass 而不是 的执行版本>MyClass(否则会干扰其他子类)。

这不是一种非常简洁的方法,但从库用户的角度来看,它应该给出所需的结果。 然后,您可以将其包装在元类中,以方便进行子类化的人。

Well the doc-string is stored in __doc__ so it wouldn't be too hard to re-assign it based on the doc-string of _execute after the fact.

Basically:

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 has to be re-declared to that the doc string gets attached to the version of execute for the SubClass and not for MyClass (which would otherwise interfere with other sub-classes).

That's not a very tidy way of doing it, but from the POV of the user of a library it should give the desired result. You could then wrap this up in a meta-class to make it easier for people who are sub-classing.

枕梦 2024-07-13 08:49:58

我同意,解决此问题的最简单、最 Python 的方法是在子类中简单地重新定义执行并让它调用基类的执行方法:

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

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

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

下面是执行此操作的代码,但请记住,我上面的示例要简单得多,也更 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!"

I agree that the simplest, most Pythonic way of approaching this is to simply redefine execute in your subclasses and have it call the execute method of the base class:

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

This is very little code to accomplish what you want; the only downside is that you must repeat this code in every subclass that extends Base. However, this is a small price to pay for the behavior you want.

If you want a sloppy and verbose way of making sure that the docstring for execute is dynamically generated, you can use the descriptor protocol, which would be significantly less code than the other proposals here. This is annoying because you can't just set a descriptor on an existing function, which means that execute must be written as a separate class with a __call__ method.

Here's the code to do this, but keep in mind that my above example is much simpler and more 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!"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文