【问题标题】:Using the docstring from one method to automatically overwrite that of another method使用一种方法的文档字符串自动覆盖另一种方法的文档字符串
【发布时间】:2010-09-09 11:11:15
【问题描述】:

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

因此,最好修改基类,使子类中execute 的文档字符串自动替换为_execute 的文档字符串。任何想法如何做到这一点?

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

【问题讨论】:

  • execute 的文档字符串不能仅仅读取“does ... 然后调用_execute,哪些子类应该被覆盖;查看_execute 的文档以了解详细信息”有什么好的理由吗?如果希望用户提供基类,这似乎是最符合 Python 的方法。
  • 如果用户键入 help(o.execute) 他会感觉有点不对劲 a) 没有得到他可能想要的信息 b) 被告知查看一些可疑的方法 _execute 可能他甚至不知道存在。对于库的天真用户来说,这只是太多的实现细节。

标签: python metaclass


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

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

    如果您想要一种草率且冗长的方法来确保动态生成执行的文档字符串,您可以使用描述符协议,这将比这里的其他提议显着减少代码。这很烦人,因为您不能只在现有函数上设置描述符,这意味着必须将 execute 编写为具有 __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!"
    

    【讨论】:

      【解决方案3】:

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

      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
      

      【讨论】:

      • 非常感谢。我认为这就是我正在寻找的,因为它对于编写子类的用户来说是完全透明的(据我所知)。
      【解决方案4】:

      是否有不能直接覆盖基类的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...的迂回方式...

      【讨论】:

      • 动机是用户编写自己的子类。我现在在原始问题中澄清了这一点。
      【解决方案5】:

      那么文档字符串存储在__doc__ 中,因此事后根据_execute 的文档字符串重新分配它不会太难。

      基本上:

      我的类(对象): def 执行(自我): '''原始文档字符串''' self._execute() 类子类(MyClass): def _execute(self): '''子类文档字符串''' 经过 # 重新分配执行的文档字符串 def 执行(自我,*args,**kw): 返回 MyClass.execute(*args,**kw) 执行.__doc__=_执行.__doc__

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

      这不是一种非常整洁的方式,但从图书馆用户的 POV 来看,它应该会给出预期的结果。然后,您可以将其包装在一个元类中,以使子类化的人更容易。

      【讨论】:

      • 问题是我希望用户编写子类而不为文档字符串操作编写任何样板代码。知道如何以比我上面提到的丑陋解决方案更好的方式处理元类吗?
      猜你喜欢
      • 1970-01-01
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-06
      • 1970-01-01
      • 1970-01-01
      • 2016-06-27
      相关资源
      最近更新 更多