继承方法';Python中的文档字符串
我有一个包含docstring的OO层次结构,它需要和代码本身一样多的维护。例如:继承方法';Python中的文档字符串,python,oop,inheritance,docstring,template-method-pattern,Python,Oop,Inheritance,Docstring,Template Method Pattern,我有一个包含docstring的OO层次结构,它需要和代码本身一样多的维护。例如: class Swallow(object): def airspeed(self): """Returns the airspeed (unladen)""" raise NotImplementedError class AfricanSwallow(Swallow): def airspeed(self): # whatever 现在的问题是
class Swallow(object):
def airspeed(self):
"""Returns the airspeed (unladen)"""
raise NotImplementedError
class AfricanSwallow(Swallow):
def airspeed(self):
# whatever
现在的问题是,airspeed
不继承超类方法的docstring。我知道我可以使用模板方法模式保存文档字符串,即
class Swallow(object):
def airspeed(self):
"""Returns the airspeed (unladen)"""
return self._ask_arthur()
并在每个子类中实现\u ask\u arthur
。然而,我想知道是否有另一种方法可以继承docstring,也许是我还没有发现的某个装饰器?这是一种变体
以类装饰器样式编写一个函数来为您进行复制。在Python2.5中,可以在创建类后直接应用它。在以后的版本中,可以使用符号进行应用 以下是如何做到这一点的第一个切入点:
import types
def fix_docs(cls):
for name, func in vars(cls).items():
if isinstance(func, types.FunctionType) and not func.__doc__:
print func, 'needs doc'
for parent in cls.__bases__:
parfunc = getattr(parent, name, None)
if parfunc and getattr(parfunc, '__doc__', None):
func.__doc__ = parfunc.__doc__
break
return cls
class Animal(object):
def walk(self):
'Walk like a duck'
class Dog(Animal):
def walk(self):
pass
Dog = fix_docs(Dog)
print Dog.walk.__doc__
在较新的Python版本中,最后一部分更简单、更美观:
@fix_docs
class Dog(Animal):
def walk(self):
pass
这是一种Python技术,与标准库中现有工具的设计完全匹配。例如,类装饰器向类添加缺少的富比较方法。另一个例子是,装饰器将元数据从一个函数复制到另一个函数。下面的自适应还处理属性和mixin类。我还遇到了一种情况,我必须使用
func.\uu func\uu
(用于“instancemethod”),但我不完全确定其他解决方案为什么没有遇到这个问题
def inherit_docs(cls):
for name in dir(cls):
func = getattr(cls, name)
if func.__doc__:
continue
for parent in cls.mro()[1:]:
if not hasattr(parent, name):
continue
doc = getattr(parent, name).__doc__
if not doc:
continue
try:
# __doc__'s of properties are read-only.
# The work-around below wraps the property into a new property.
if isinstance(func, property):
# We don't want to introduce new properties, therefore check
# if cls owns it or search where it's coming from.
# With that approach (using dir(cls) instead of var(cls))
# we also handle the mix-in class case.
wrapped = property(func.fget, func.fset, func.fdel, doc)
clss = filter(lambda c: name in vars(c).keys() and not getattr(c, name).__doc__, cls.mro())
setattr(clss[0], name, wrapped)
else:
try:
func = func.__func__ # for instancemethod's
except:
pass
func.__doc__ = doc
except: # some __doc__'s are not writable
pass
break
return cls
F.Y.I对于刚刚在这个主题上结结巴巴的人来说:从Python3.5开始,自动从继承层次结构中检索docstring 因此,上面的响应对于Python2非常有用,或者如果您希望在合并父对象和子对象的docstring时更具创造性的话
我也创造了一些。它们支持一些很好的默认文档字符串样式(numpy、google、reST)。您也可以轻松地使用自己的docstring样式仅此示例就值得+1(您在官方文档之外看到的Python引用太少了)。幸运的是,剩下的问题也证明了投票的合理性;)看一看——有很多解决方案。应该可以编写一个类修饰符,遍历所有的方法,看看它们的
\uuuuuuuuuuuu
是否为None
,如果是,则借用super\uuuuuuuuuuuuu
。现在没有时间尝试。。。另请参见@wberry:class decorators不是一个选项,因为我的目标是Python2.5。对不起,我应该早点说。@NeilG:我更新了代码,使之与Python3兼容。(现在)唯一需要的更改是用类Baz(Bar,metaclass=docStringInheritator)
定义Baz
,而不是在类体中定义\uuuuuuu metaclass=docStringInheritator
。如果有一个没有缺点的更简单的解决方案,这就太过了。如果派生类具有属性(Python 2.7.9),就会崩溃。请看:@RedX:谢谢你让我注意到这一点。由于属性\uuuuu doc\uuuuuu
属性不能通过重新分配来修改,因此我修改了上面的代码,通过将class属性重新分配给一个新属性(使用旧的getter、setter、deleter,但使用一个新的docstring),来处理属性。我希望将由此派生的代码合并到MIT许可库中。您是否愿意在MIT兼容许可下重新授权您的代码,以便我可以合法地这样做?此答案包含一个错误vars(cls)
包含类中的一对“文档”:None
,它在if func.\uu文档
中引发一个AttributeError
。应跳过该项或将其置于特殊情况下。也是将继承属性描述的版本。
def inherit_docs(cls):
for name in dir(cls):
func = getattr(cls, name)
if func.__doc__:
continue
for parent in cls.mro()[1:]:
if not hasattr(parent, name):
continue
doc = getattr(parent, name).__doc__
if not doc:
continue
try:
# __doc__'s of properties are read-only.
# The work-around below wraps the property into a new property.
if isinstance(func, property):
# We don't want to introduce new properties, therefore check
# if cls owns it or search where it's coming from.
# With that approach (using dir(cls) instead of var(cls))
# we also handle the mix-in class case.
wrapped = property(func.fget, func.fset, func.fdel, doc)
clss = filter(lambda c: name in vars(c).keys() and not getattr(c, name).__doc__, cls.mro())
setattr(clss[0], name, wrapped)
else:
try:
func = func.__func__ # for instancemethod's
except:
pass
func.__doc__ = doc
except: # some __doc__'s are not writable
pass
break
return cls
def fix_docs(cls):
""" copies docstrings of derived attributes (methods, properties, attrs) from parent classes."""
public_undocumented_members = {name: func for name, func in vars(cls).items()
if not name.startswith('_') and not func.__doc__}
for name, func in public_undocumented_members.iteritems():
for parent in cls.mro()[1:]:
parfunc = getattr(parent, name, None)
if parfunc and getattr(parfunc, '__doc__', None):
if isinstance(func, property):
# copy property, since its doc attribute is read-only
new_prop = property(fget=func.fget, fset=func.fset,
fdel=func.fdel, doc=parfunc.__doc__)
cls.func = new_prop
else:
func.__doc__ = parfunc.__doc__
break
return cls