在Python中复制基类'__init__'签名的正确方法是什么?

2024-02-06

如果子类没有定义自己的__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

Child1没有自己的构造函数,因此Base's __init__是继承的,并且help(Child1)会给出:

Help on class Child1 in module test:

class Child1(Base)
 |  Child1(arg1, arg2)
 |  
 ...

Child2需要有一个定制__init__。然而,因为它只是将所有参数传递给Base,其参数定义为(*args, **kwargs)以便将所有东西短路Base。但这给出了以下签名:

Help on class Child2 in module test:

class Child2(Base)
 |  Child2(*args, **kwargs)
 |  
 ...

其信息量要少得多。有没有办法说“我想要我的__init__具有相同的签名Base.__init__"?


这个答案描述了我如何找到一个合理的解决方案,所以请耐心等待,或者直接滚动到最后的 TL;DR'd 装饰器。

让我们首先看看什么help https://docs.python.org/3/library/functions.html#help是并且确实是。help通过以下方式添加到内置命名空间site https://docs.python.org/3/library/site.html。我的 Ubuntu 机器上的默认实现将所有内容重定向到pydoc.help https://docs.python.org/3/library/pydoc.html。这又使用inspect https://docs.python.org/3/library/inspect.html获取签名和描述。您只对函数感兴趣,更具体地说__init__。另外,您只关心签名,而不关心文档的其余部分。这应该会让事情变得更简单。

我们可以放心地假设您在中看到的签名help/pydoc是由生成的inspect.signature https://docs.python.org/3/library/inspect.html#inspect.signature。看着源代码 https://github.com/python/cpython/blob/v3.8.2/Lib/inspect.py#L3091Python 3.8.2 的该函数,并跟踪inspect.Signature.from_callable https://github.com/python/cpython/blob/v3.8.2/Lib/inspect.py#L2840 -> inspect._signature_from_callable https://github.com/python/cpython/blob/v3.8.2/Lib/inspect.py#L2206 -> 2246号线 https://github.com/python/cpython/blob/v3.8.2/Lib/inspect.py#L2246,我们看到了一个可能的解决方案。

它的要点是,如果一个函数对象有一个__signature__属性,并且该属性是一个实例inspect.Signature https://docs.python.org/3/library/inspect.html#inspect.Signature,它将用作函数的签名,而无需从正常检查中重新计算__code__ https://docs.python.org/3/library/inspect.html#types-and-members and __annotation__对象。

我们赞成的另一点是函数是具有__dict__ https://docs.python.org/3/library/stdtypes.html#object.__dict__可以为其分配任意键的属性。分配__signature__添加到您的函数不会影响其执行,因为它仅用于检查。实际的运行时签名是在__code__对象通过属性如co_argcount, co_kwonlyargcount, co_varnames, etc.

因此你可以这样做:

import inspect

Child2.__init__.__signature__ = inspect.signature(Base.__init__)

结果:

>>> 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)

除此之外,两个构造函数都继续照常运行。

由于这不会修改代码对象甚至注释,因此更改不太可能影响函数操作。

TL;DR

这是一个装饰器,您可以使用它来复制函数签名,而不会以任何其他方式干扰该函数:

import inspect

def copy_signature(base):
    def decorator(func):
        func.__signature__ = inspect.signature(base)
        return func
    return decorator

你可以重写Child2 as

class Child2:

    @copy_signature(Base.__init__)
    def __init__(self, *args, **kwargs):
        ...
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在Python中复制基类'__init__'签名的正确方法是什么? 的相关文章

随机推荐

  • jQuery UI 可排序 - 对图像进行排序

    我刚刚为一组图像实现了 jQuery UI 可排序插件 我的标记如下 ul class ui sortable li img src images member 4698568 7884029 t jpg alt li li img src
  • WebBrowser 控件不会从 C# 打印

    我在 WinForms 应用程序上有一个 WebBrowser 控件 它正在加载转换为 HTML CSS 的 XML 如果我只想在那里或在常规浏览器中查看它 看起来很漂亮 当表单加载时 它应该导航到该文件 然后当 OnDocumentCom
  • 如何使用ssr在nuxt中添加ckeditor插件

    我正在尝试在我的通用 nuxt 应用程序中添加 ckeditor 5 的对齐插件 SSR 我在插件中尝试过这样 import Vue from vue import ClassicEditor from ckeditor ckeditor5
  • Jekyll编码类别特殊字符名称

    我的 Jekyll 安装曾经可以工作 自更新以来 我遇到了 URL 包含带有一些特殊字符的标签名称的问题 现在 当我尝试访问包含特殊字符的 URL 时 会收到一条错误消息 例如http 127 0 0 1 4000 tag Actualit
  • 最小成本强连通有向图

    我有一个强连接的有向图 即图 G 中的每对节点 i j 都有一条从 i 到 j 和 j 到 i 的路径 我希望从该图中找到一个强连通图 使得所有边的总和最小 换句话说 我需要以这样的方式删除边 即删除它们后 图仍然是强连接的 并且边总和的成
  • 反应具有比例依赖性的本机动画旋转圆圈

    我有一个动画组件 您可以在其中选择十七个圆圈之一 到目前为止看起来像这样 我想添加一个动画 当圆靠近中心时 它会缩放圆 我怎么做 到目前为止 我尝试将圆的 x 值计算为Math sin index deltaTheta Math PI 18
  • TPL 取消延续从未调用已取消的任务

    我的代码中有以下使用 TPL 的设置 我的班级中有一个字段 private CancellationTokenSource cancellationTokenSource 每次我创建使用特定取消令牌的 TPL 任务时 都会实例化此 Canc
  • 加载配置文件时发生错误:无法开始监视“\\share”的更改

    上周我也遇到了同样的问题 在启用模拟后它自行解决了 现在 我已将此盒子移至其他位置 现在再次收到它 然而 这一次的描述性要少得多 我收到的唯一错误是 An error occurred loading a configuration fil
  • boost::make_shared 没有调用(放置)运算符 new 吗?

    我第一次使用 boost make shared 创建共享指针指向的对象 主要是因为我们的代码太慢了 而单一分配确实有助于提高性能 在 硬手动方式 修复了一些内存泄漏之后 我决定通过覆盖所有相关类的新运算符来实现一个简单的内存泄漏检测器 只
  • 未遵循 Java log4j2 记录器级别

    所以我正在尝试学习 log4j2 并关注记录器及其级别和父级传播 目前我的源层次结构运行是 main java calculatorMain Main java someClass2 java someClass1 java resourc
  • PHP:选择添加到 Amazon S3 文件夹的最新文件

    我正在开发自动更新解决方案 并使用 Amazon S3 进行分发 我希望它的工作方式如下 我将文件上传到 s3 文件夹 自动 PHP 脚本检测到已添加新文件并通知客户端 为此 我需要以某种方式列出亚马逊存储桶文件夹中的所有文件 并找到最后添
  • 将 Windows 设备路径解析为驱动器盘符

    如何解析 NT 样式的设备路径 例如 Device CdRom0 到其逻辑驱动器号 例如G 编辑 不幸的是 卷名称与设备路径不同GetVolumePathNamesForVolumeName 行不通的 希望下面的代码足以解决这个问题 在初始
  • Linux异步IO的状态?

    我在这里问这个问题是因为谷歌搜索会让你在档案馆中进行一次愉快的旅行 但没有任何关于当前状态的提示 如果你通过 Google 进行搜索 你会发现异步 IO 在 2001 年到 2003 年期间非常流行 到 2006 年 一些类似的东西epol
  • 仅允许特定电子邮件地址在 Rails 应用程序中注册 (Devise)

    我正在使用 Devise 在我的 Rails 应用程序中验证和注册用户 但是 我只希望拥有特定结尾电子邮件的用户能够注册并访问它 比方说 xyz com 我需要做什么来反映这一点 如果您想在注册后限制用户访问 请使用 before filt
  • 在 C# 中从图像的 EXIF 获取 GPS 数据

    我正在开发一个系统 允许使用 ASP NET C 将图像上传到服务器 我正在处理图像 一切正常 我设法找到一种方法来读取创建日期 EXIF 数据并将其解析为日期时间 这也很好用 我现在正在尝试从 EXIF 读取 GPS 数据 我想捕获纬度和
  • 使用 pg-promise 记录特定的 postgresql 查询

    我在用pg 承诺 https github com vitaly t pg promise与 Nodejs 一起打包以执行 PostgreSQL 查询 我想查看执行的查询 仅特定查询 例如 我想调试的一个查询 我可以看到一种推荐的方法是使用
  • 整数值比较

    我是一名 Java 编码新手 我刚刚读到可以在 API 中用三种不同的方式描述整数类的变量 我有以下代码 if count compareTo 0 System out println out table count 这是在循环内部并且仅输
  • 当弹性项目换行到新行时,我可以将 CSS 应用于它吗?

    wrapper border 5px solid pink display flex flex wrap wrap justify content center a fc background color purple width 300p
  • NSTokenField 不允许我输入 tokenField:completionsForSubstring:... 之外的其他字符串返回

    我的问题是 NSTokenField 不允许我输入我想要的任何文本 它只允许我输入 tokenField completionsForSubstring indexOfToken indexOfSelectedItem 返回的 NSArra
  • 在Python中复制基类'__init__'签名的正确方法是什么?

    如果子类没有定义自己的 init 方法时 基类的构造函数 因此其签名 会自动继承 但是应该如何定义子类 init 继承基类签名的方法 自动 例如 class Base def init self arg1 arg2 self arg1 ar