kMa*_*ter 6 python inheritance python-3.x
如果子类没有定义自己的__init__方法,则自动继承基类的构造函数(因此其签名)。但是应该如何定义一个__init__继承基类签名(自动)的子类方法呢?
例如:
class Base:
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
class Child1(Base):
def foo(self):
return self.arg1 + self.arg2
class Child2(Base):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.arg1 == 0:
raise ValueError("arg1 cannot be zero")
def foo(self):
return self.arg2 / self.arg1
Run Code Online (Sandbox Code Playgroud)
Child1没有自己的构造函数,因此Bases __init__被继承,并且help(Child1)会给出:
Help on class Child1 in module test:
class Child1(Base)
| Child1(arg1, arg2)
|
...
Run Code Online (Sandbox Code Playgroud)
Child2需要有一个习惯__init__。然而,由于它只是将所有参数传递给Base,因此它的参数被定义(*args, **kwargs)为将所有参数短路到Base。但这给出了以下签名:
Help on class Child2 in module test:
class Child2(Base)
| Child2(*args, **kwargs)
|
...
Run Code Online (Sandbox Code Playgroud)
其信息量要少得多。有没有办法说“我希望我的__init__签名与”相同Base.__init__?
这个答案描述了我如何找到一个合理的解决方案,所以请耐心等待,或者滚动到最后的 TL;DR'd 装饰器。
让我们首先看看是什么help和做了什么。help被添加到内置命名空间中site。我的 Ubuntu 机器上的默认实现将所有内容重定向到pydoc.help. 这反过来又用于inspect获取签名和描述。您只对函数感兴趣,更具体地说__init__。另外,您只关心签名,而不关心文档的其余部分。这应该会让事情变得更简单。
help我们可以放心地假设您在/中看到的签名pydoc是由 生成的inspect.signature。查看Python 3.8.2 的该函数的源代码,并跟踪inspect.Signature.from_callable-> inspect._signature_from_callable->第 2246 行,我们看到了一个可能的解决方案。
其要点是,如果函数对象具有属性__signature__,并且该属性是 的实例inspect.Signature,则它将用作函数的签名,而无需从 和__code__对象的正常检查中重新计算它__annotation__。
我们赞成的另一点是函数是一流的对象,__dict__其属性可以分配任意键。分配__signature__给您的函数不会影响其执行,因为它仅用于检查。实际的运行时签名是__code__通过co_argcount、co_kwonlyargcount、co_varnames等属性在对象中确定的。
因此你可以这样做:
import inspect
Child2.__init__.__signature__ = inspect.signature(Base.__init__)
Run Code Online (Sandbox Code Playgroud)
结果:
>>> help(Child1)
Help on class Child1 in module __main__:
class Child1(Base)
| Child1(arg1, arg2)
|
| Method resolution order:
| Child1
| Base
| builtins.object
|
| Methods defined here:
|
| foo(self)
|
| ----------------------------------------------------------------------
| Methods inherited from Base:
|
| __init__(self, arg1, arg2)
| Initialize self. See help(type(self)) for accurate signature.
|
| ----------------------------------------------------------------------
| Data descriptors inherited from Base:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
>>> help(Child2)
Help on class Child2 in module __main__:
class Child2(Base)
| Child2(arg1, arg2)
|
| Method resolution order:
| Child2
| Base
| builtins.object
|
| Methods defined here:
|
| __init__(self, arg1, arg2)
| Initialize self. See help(type(self)) for accurate signature.
|
| foo(self)
|
| ----------------------------------------------------------------------
| Data descriptors inherited from Base:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
Run Code Online (Sandbox Code Playgroud)
除此之外,两个构造函数都继续照常运行。
由于这不会修改代码对象甚至注释,因此更改不太可能影响函数操作。
长话短说
这是一个装饰器,您可以使用它来复制函数签名,而不会以任何其他方式干扰该函数:
import inspect
def copy_signature(base):
def decorator(func):
func.__signature__ = inspect.signature(base)
return func
return decorator
Run Code Online (Sandbox Code Playgroud)
你可以重写Child2为
class Child2:
@copy_signature(Base.__init__)
def __init__(self, *args, **kwargs):
...
Run Code Online (Sandbox Code Playgroud)