假设我有两个类(A
和B
),其中第二个类是从第一个类派生的。此外,我通过在B中编写新的实现来隐藏在A中实现的某些属性。但是,我为A编写的文档字符串对B仍然有效,而且我很懒惰-我不想复制粘贴所有内容。
请注意,这里的关键问题是我的目标是属性,对于一般的类方法,都会发布解决方案。
在代码中,最小的类示例如下所示:
In [1]: class A(object):
...: @property
...: def a(self):
...: "The a:ness of it"
...: return True
...:
In [2]: class B(A):
...: @property
...: def a(self):
...: return False
...:
我基本上想要的是有一种方法来实现以下目标:
In [8]: B.a.__doc__
Out[8]: 'The a:ness of it'
实际上,B.a的文档字符串为空,并且无法写入
B.a.__doc__
,因为它会引发TypeError
。据我所知是以下解决方案:
from inspect import getmro
def inheritDocFromA(f):
"""This decorator will copy the docstring from ``A`` for the
matching function ``f`` if such exists and no docstring has been added
manually.
"""
if f.__doc__ is None:
fname = f.__name__
for c in getmro(A):
if hasattr(c, fname):
d = getattr(c, fname).__doc__
if d is not None:
f.__doc__ = d
break
return f
这确实有效,但由于
A
被硬编码到装饰器功能中而变得难看当传递给装饰器时,无法知道
f
所附加的类:In [15]: class C(A):
....: @property
....: @inheritDocFromA
....: def a(self):
....: return False
....:
In [16]: C.a.__doc__
Out[16]: 'The a:ness of it'
问:是否可以为类属性上的装饰器应用文档字符串构造通用解决方案,而无需在装饰器函数的继承中进行硬编码?
我也尝试过装饰类,但是后来我的属性docstrings被写保护了。
最后,如果可能的话,我希望该解决方案适用于Python 2.7和3.4。
最佳答案
可以编写通过类访问时返回正确的__doc__
的装饰器-毕竟,__get__
接收该类型并可以通过其MRO,找到合适的__doc__
,然后对其进行设置(或在其上为此目的创建并返回的代理)。但是,解决__doc__
不可写的问题要简单得多。
事实证明,由于property
是作为一种类型实现的,因此使其实例的__doc__
可写就像从其继承一样简单:
class property_writable_doc(property):
pass
然后,使用类装饰器继承属性的
__doc__
的想法可以起作用:def inherit_doc_class(cls):
for name, obj in cls.__dict__.iteritems():
if isinstance(obj, property_writable_doc) and obj.__doc__ is None:
for t in cls.__mro__:
if name in t.__dict__ and t.__dict__[name].__doc__ is not None:
obj.__doc__ = t.__dict__[name].__doc__
break
return cls
class A(object):
@property
def a(self):
"The a:ness of it"
return True
@inherit_doc_class
class B(A):
@property_writable_doc
def a(self):
return False
@inherit_doc_class
class C(A):
@property_writable_doc
def a(self):
"The C:ness of it"
return False
关于python - 如何使用装饰器自动记录我的类(class)属性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24406190/