我有一个简单的装饰器的想法,它可能不是很有用(请随意评论,但这不是我的主要关注点)。不管怎样,我认为它会展示如何实现某些目标,而这些是我的主要兴趣。
想法: 一个装饰器
@inherit_docs
,它将简单地从具有它的类的第一个父级中具有相同名称的方法分配方法的__doc__
。
例如,假设我们有这个:
class A:
def foo(self):
"""
Return something smart.
"""
return ...
现在,我想要这个:
class B(A):
@inherit_docs
def foo(self):
# Specifically for `B`, the "smart" thing is always 17,
# so the docstring still holds even though the code is different.
return 17
相当于:
class B(A):
def foo(self):
"""
Return something smart.
"""
# Specifically for `B`, the "smart" thing is always 17,
# so the docstring still holds even though the code is different.
return 17
这样做的一个目的可能是让一个基类声明一些功能,然后它的继承类为不同类型的数据实现这个功能(多态性)。这些方法做同样的事情,所以自动化文档字符串一致性会很好。 ?
这里的问题(也是我对它感兴趣的主要原因)是:
help
怎么知道它在哪个班级(所以,它现在怎么知道B.foo.__doc__
)?
当
B.foo
None
inherit_docs
本身被调用时做一些事情。这通常可以正常工作(因为调用会得到 B
@inherit_docs
@inherit_docs
或
foo
不要调用
self
本身。
这甚至可能吗?如果可以,怎么办?
我们可以使用
type(self)
查找
B.foo.__doc__
的父母,然后通过help(B.foo)
mro()
'但对于类
B
),获取文档字符串,然后在装饰器
dict
中指定
some_method
的
foo
为父级:
A
“遍历”方法解析顺序并找到“最后”父级(即假设有类
some_method
继承自
__doc__
,
wrapper
继承自def inherit_docs(some_method):
def wrapper(self):
a = self.__class__.mro()
wrapper.__doc__ = a[-2].__dict__[some_method.__name__].__doc__
return some_method(self)
return wrapper
,......全部或部分其中包含名为'C
'的
B
,我们在类
D
,
C
,...中使用这个装饰器,并希望将其
some_method
分配给类
foo
)然后:
C
@matszwecja 的提示看this answer
是正确的。这是似乎有效的完整解决方案:
D
解决方案仍然可以使用
与另一个问题的解决方案的主要区别在于,另一个解决方案中的装饰器将自身替换为装饰方法,而这个定义了A
Namespace
def inherit_docs(some_method):
def wrapper(self):
a = self.__class__.mro()
a = [value for index, value in enumerate(a) if value.__dict__.__contains__(some_method.__name__) and index != 0][-1]
wrapper.__doc__ = a.__dict__[some_method.__name__].__doc__
return some_method(self)
return wrapper
已定义(因此,“将有一个类
class inherit_docs:
def __init__(self, method):
self._method = method
def __set_name__(self, owner, name):
print(f"decorating {self._method} and using {owner}")
try:
inherited_method = next(
inherited_method
for inherited_method in (
parent.__dict__.get(self._method.__name__)
for parent in reversed(owner.mro())
)
if inherited_method and inherited_method.__doc__ is not None
)
except StopIteration:
pass
else:
self.__doc__ = inherited_method.__doc__
def __call__(self, *args, **kwargs):
return self._method(self, *args, **kwargs)
class A:
def foo(self):
"""
Doctring in A.
"""
return 19
class B(A):
@inherit_docs
def foo(self):
return 17
print(B.foo.__doc__)
print(B.foo())
和一个方法 wrap
__call__
然后,当编译器到达
B
代码的末尾时,类
B
实际上被创建了。
foo
的所有属性,并为任何具有它的属性调用
class B:...
。
class B:...
实际上只是
B
的快捷方式,所以
B
实际上是__set_name__
@inherit_docs
,它在创建类
foo = inherit_docs(foo)
时被调用,它用docstring完成所有的魔法(正如@OriYarden在
answer中解释的那样;我所做的是一个简单的重构).
最后,因为
B.foo
有
inherit_docs
,它在调用__set_name__
时被调用,它只是将调用传播到保存为B
因此,在某种程度上,
inherit_docs
是事件“您的类(不是该类的实例!)刚刚创建”的每个属性的事件处理程序。