自定义方法装饰器知道一个类[重复]

问题描述 投票:0回答:2

我有一个简单的装饰器的想法,它可能不是很有用(请随意评论,但这不是我的主要关注点)。不管怎样,我认为它会展示如何实现某些目标,而这些是我的主要兴趣。

想法: 一个装饰器

@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
    执行时类本身不存在时,它怎么能得到关于类的任何信息(在这种情况下,它是父母)?
  1. 我可能 MacGyver 有元类的东西,但这意味着装饰器只适用于继承一些专门为此目的制作的类的类,这是丑陋的。
    我也可以让它在 
  2. None
  3. 被调用时不做任何事情,但是在
    inherit_docs
    本身被调用时做一些事情。这通常可以正常工作(因为调用会得到
  4. B
然后玩它或与

@inherit_docs

),但在这种情况下这无济于事,因为

@inherit_docs

foo
 不要调用 
self
本身。
这甚至可能吗?如果可以,怎么办?

我们可以使用
type(self)
查找
B.foo.__doc__

的父母,然后通过

help(B.foo)

搜索
python python-decorators python-class
2个回答
1
投票
的名字(即'

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
解决方案仍然可以使用

0
投票
以获得正确的方法签名,但这超出了这个问题的范围。

与另一个问题的解决方案的主要区别在于,另一个解决方案中的装饰器将自身替换为装饰方法,而这个定义了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__
  1. 位。

    然后,当编译器到达
    B
    代码的末尾时,类
    B
    实际上被创建了。

  2. 此时,检查

    foo

    的所有属性,并为任何具有它的属性调用
    class B:...

  3. 由于调用装饰器

    class B:...

    实际上只是
    B
    的快捷方式,所以
    B

    实际上是
  4. __set_name__
  5. 类的一个实例。这个实例有

    @inherit_docs

    ,它在创建类
    foo = inherit_docs(foo)
    时被调用,它用docstring完成所有的魔法(正如@OriYarden
    answer中解释的那样;我所做的是一个简单的重构).
    最后,因为
    B.foo
    inherit_docs,它在调用__set_name__

    时被调用,它只是将调用传播到保存为
  6. B
  7. 的原始方法。

    
    
    因此,在某种程度上,
    inherit_docs
     是事件“您的类(不是该类的实例!)刚刚创建”的每个属性的事件处理程序。

© www.soinside.com 2019 - 2024. All rights reserved.