super() 并更改协作方法的签名

2023-12-28

在多重继承设置中,例如所列出的,我该如何使用super()并处理层次结构中的类之间函数签名发生变化的情况?

即我可以重写这个例子(在 python3 中)来使用super()?

例子取自文章super() 被视为有害文章 https://fuhm.net/super-harmful/example1-1.py

class A():
    def __init__(self):
        print("A")

class B(object):
    def __init__(self):
        print("B")

class C(A):
    def __init__(self, arg):
        print("C","arg=",arg)
        A.__init__(self)

class D(B):
    def __init__(self, arg):
        print("D", "arg=",arg)
        B.__init__(self)

class E(C,D):
    def __init__(self, arg):
        print("E", "arg=",arg)
        C.__init__(self, arg)
        D.__init__(self, arg)

E(10)


詹姆斯·奈特的文章super() 被认为是有害的 https://fuhm.net/super-harmful/通过始终接受来提出解决方案*args and **kwargs在所有合作职能中。 然而这个解决方案不起作用有两个原因:

  1. object.__init__不接受争论 这是 python 2.6 / 3.x 引入的重大更改TypeError: object.__init__() takes no parameters https://menno.io/posts/object__init__takes_no_parameters/

  2. using *args实际上是适得其反的

解决方案 TL;DR

  1. super()用法必须一致:在类层次结构中, super 应该在任何地方使用,或者不在任何地方使用。是班级合同的一部分。如果一个类使用super()所有课程MUST也使用super()以同样的方式,否则我们可能会调用层次结构中的某些函数零次或多次

  2. 正确支持__init__带有任何参数的函数,层次结构中的顶级类必须继承自自定义类,例如 SuperObject:

    class SuperObject:        
        def __init__(self, **kwargs):
            mro = type(self).__mro__
            assert mro[-1] is object
            if mro[-2] is not SuperObject:
                raise TypeError(
                    'all top-level classes in this hierarchy must inherit from SuperObject',
                    'the last class in the MRO should be SuperObject',
                    f'mro={[cls.__name__ for cls in mro]}'
                )
    
            # super().__init__ is guaranteed to be object.__init__        
            init = super().__init__
            init()
    
  3. 如果类层次结构中的重写函数可以采用不同的参数,则始终将收到的所有参数作为关键字参数传递给 super 函数,并且始终接受**kwargs.

这是一个重写的例子

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

e = E(name='python', age=28)

output:

E name=python age=28
C age=28
A
D name=python
B
SuperObject

讨论

让我们更详细地看看这两个问题

object.__init__不接受争论

考虑 James Knight 给出的原始解决方案:

一般规则是:始终将收到的所有参数传递给 super 函数,并且,如果类可以采用不同的参数,则始终接受*args and **kwargs.

    class A:
        def __init__(self, *args, **kwargs):
            print("A")
            super().__init__(*args, **kwargs)

    class B(object):
        def __init__(self, *args, **kwargs):
            print("B")
            super().__init__(*args, **kwargs)

    class C(A):
        def __init__(self, arg, *args, **kwargs):
            print("C","arg=",arg)
            super().__init__(arg, *args, **kwargs)

    class D(B):
        def __init__(self, arg, *args, **kwargs):
            print("D", "arg=",arg)
            super().__init__(arg, *args, **kwargs)

    class E(C,D):
        def __init__(self, arg, *args, **kwargs):
            print( "E", "arg=",arg)
            super().__init__(arg, *args, **kwargs)

    print( "MRO:", [x.__name__ for x in E.__mro__])
    E(10)

python 2.6 和 3.x 中的重大更改已更改object.__init__签名,使其不再接受任意参数

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-9001c741f80d> in <module>
     25 
     26 print( "MRO:", [x.__name__ for x in E.__mro__])
---> 27 E(10)

...

<ipython-input-2-9001c741f80d> in __init__(self, *args, **kwargs)
      7     def __init__(self, *args, **kwargs):
      8         print("B")
----> 9         super(B, self).__init__(*args, **kwargs)
     10 
     11 class C(A):

TypeError: object.__init__() takes exactly one argument (the instance to initialize)

处理这个难题的正确方法是让层次结构中的顶级类继承自自定义类,例如SuperObject:

class SuperObject:        
    def __init__(self, *args, **kwargs):
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

因此重写示例如下应该可行

    class A(SuperObject):
        def __init__(self, *args, **kwargs):
            print("A")
            super(A, self).__init__(*args, **kwargs)

    class B(SuperObject):
        def __init__(self, *args, **kwargs):
            print("B")
            super(B, self).__init__(*args, **kwargs)

    class C(A):
        def __init__(self, arg, *args, **kwargs):
            print("C","arg=",arg)
            super(C, self).__init__(arg, *args, **kwargs)

    class D(B):
        def __init__(self, arg, *args, **kwargs):
            print("D", "arg=",arg)
            super(D, self).__init__(arg, *args, **kwargs)

    class E(C,D):
        def __init__(self, arg, *args, **kwargs):
            print( "E", "arg=",arg)
            super(E, self).__init__(arg, *args, **kwargs)

    print( "MRO:", [x.__name__ for x in E.__mro__])
    E(10)

output:

MRO: ['E', 'C', 'A', 'D', 'B', 'SuperObject', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B
SuperObject

using *args是适得其反的

让这个例子变得更复杂一些,有两个不同的参数:name and age

class A(SuperObject):
    def __init__(self, *args, **kwargs):
        print("A")
        super(A, self).__init__(*args, **kwargs)

class B(SuperObject):
    def __init__(self, *args, **kwargs):
        print("B")
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, age, *args, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age, *args, **kwargs)

class D(B):
    def __init__(self, name, *args, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name, *args, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name, age, *args, **kwargs)

E('python', 28)

output:

E name=python age=28
C age=python
A
D name=python
B
SuperObject

正如你从线上看到的C age=python位置参数变得混乱,我们传递了错误的东西。

我建议的解决方案是更加严格并避免*args完全论证。反而:

如果类可以采用不同的参数,请始终将收到的所有参数传递给 super 函数作为关键字参数,并且,始终接受**kwargs.

这是基于此更严格规则的解决方案。首先删除*args from SuperObject

class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

现在删除*args来自其余类,并仅按名称传递参数

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

output:

E name=python age=28
C age=28
A
D name=python
B
SuperObject

哪个是对的

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

super() 并更改协作方法的签名 的相关文章

随机推荐

  • 检测是否开启“蓝牙扫描”定位

    从 Android M 开始 如果您启用了蓝牙设备 即使全球定位已关闭 也可以在后台扫描蓝牙设备 蓝牙扫描位置设置中的选项 参见屏幕截图 为了扫描 BLE 设备 必须满足以下条件 COARSE LOCATION or FINE LOCATI
  • 使用 C# 将布尔数组转换为字符串

    我有一个看起来像这样的数组 status 0 true status 1 true status 2 false status 3 true 实际上它更大 但仍然小于 20 我需要将其转换为 ABD 其中每个 true 代表字母表中的一个有
  • 使用芯片输入选择不显示所选值

    我有一个选择 输入采用芯片格式 我尝试了所选值的控制台日志 效果很好 但由于某种原因 它没有显示在选择框中 我在这里做错了什么 handleChange event gt this setState badge event target v
  • Spring Boot中事务同步与Database+kafka示例

    我想使用 Spring boot 编写一个新应用程序 使用 MySQL Mango 数据库和 Spring Kafka 进行消息传递 我尝试使用 Many POC 来同步 Kafka 和 DB 之间的事务 但在某些情况下失败了 并且我还搜索
  • 如何访问 CGContext 以在屏幕外创建 CGLayer

    意图 创造CGLayer在屏幕外 从第二个线程在其上绘制一些复杂的东西 稍后使用它在主线程上快速绘制 Problem CGLayerCreateWithContext context size info 期望已经存在的CGContext这样
  • 如何在 ASP.net IIS 7.5 中利用浏览器缓存

    以下可缓存资源的新鲜生命周期较短 指定以下资源至少在未来一周内到期 http pagespeed googlelabs com http pagespeed googlelabs com建议我将此作为我的网站的高度优先事项 我正在使用 Wi
  • 用于 GUI 的 Java 2D 场景图形库

    我正在寻找用于构建 GUI 应用程序的 2D 场景图形库 它应该很快 使用 Java2D 并与 Swing 兼容 可嵌入 Swing 应用程序并显示 Swing 组件 对动画的支持是一个优点 您有使用某些图书馆的经验吗 有没有我错过的图书馆
  • Numpy 图像数组:如何有效地从 RGB 切换到 Hex

    我一直在使用嵌套 for 循环将 RGB 图像转换为十六进制值的图像数组 但对于大图像来说太慢了 有谁知道可以帮助我从 RGB 切换回 HEX 的快速方法和 或库 编辑 ragingSloth 这是我想出的 但对于我的需要来说太慢了 def
  • 如何在 Travis CI 上安装某些东西而不超时?

    我正在尝试测试 travis ci org 上构建的包 但 pip install scipy 遇到超时 Installing collected packages scipy Running setup py install for sc
  • 如何从 Excel 单元格获取完整值,而不是显示的(四舍五入)值?

    我在从工作表的单元格检索确切值时遇到问题 如果我打开文件 单元格有一个十进制数 仅显示 4 位小数 但如果我单击特定单元格 则该值会不同 有 6 位小数 我知道这是应用于单元格的设置 以便仅显示 4 位小数 现在我尝试使用 ClosedXM
  • WPF中如何移动焦点?

    我想知道是否有办法将焦点从当前控件移至 WPF 中 TabIndex 分配的控件上的其他控件 例子 我有 TabIndex 1 到 5 的控件 有没有办法将焦点从 1 跳转到 5
  • 从 C# 类设置 Gridview“标题”的样式

    我正在尝试在 C 文件中设置 ASP Net GridView 标题的样式 这是我返回样式化 GridView 的方法 private GridView setupGridView string caption var gview new
  • 检查context.Request.Body的条件是azure api管理策略中的JArray或JObject

    我正在使用 Azure API 管理策略表达式将供应商值发送到每个帖子 对后端 API 的放置和删除请求 我编写了一个代码 当请求类型为 JObject 时 该代码工作正常 但我有一些请求可以是 JArray 类型的情况 在这种情况下它会抛
  • 如何在IIS7中禁用应用程序池空闲超时?

    如果我将空闲超时设置为0 它会被禁用吗 是的 将空闲超时值设置为零将禁用空闲超时 奇怪的是 这没有记录在 MS 文档中 但我的证据来自 IIS 设置架构 如果您查看了 IIS 设置架构 C Windows System32 inetsrv
  • 如何安装:OpenSSL + WAMP

    这里有人有关于如何在我的本地开发计算机上获取 SSL 的清晰详细的步骤吗 我已在 c wamp 上安装了最新版本的 WAMP 2 2c 我在网上找到的说明在大多数情况下似乎已经过时 并且缺乏正确完成工作所需的详细信息 指南 WampServ
  • wl-pprint-extras 中的可选空格和多个替代项

    是否有一种好的 可接受的方法来获得两种以上的替代布局 特别是支持可选空间的方式 特别是 我可能想通过以下三种方式之一打印列表 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 按照该优先顺序 您可以先尝试一种替代方案 然后再尝试另
  • 从 FireWire 获取视频流

    我正在尝试通过 FireWire 将摄像机 Sony HVR Z1E 中的视频流式传输到我的计算机 传入的图片 流应由一些期望 CVMat 格式 来自 openCV 的函数进一步处理 好吧 我现在的问题是我不知道如何获取流 好吧 openC
  • Istio 分布式跟踪与 Jaeger 不起作用

    我正在尝试设置本地 k8s 集群minikube已安装istio我在使用 Jaeger 启用分布式跟踪时遇到问题 我有3个微服务A gt B gt C 我正在传播所需的所有标头 x request id x b3 traceid x b3
  • escapeshellarg 和 escapeshellcmd 有什么区别?

    PHP 有 2 个密切相关的函数 escapeshellarg http php net escapeshellarg and escapeshellcmd http php net escapeshellcmd 它们似乎都做类似的事情 即
  • super() 并更改协作方法的签名

    在多重继承设置中 例如所列出的 我该如何使用super 并处理层次结构中的类之间函数签名发生变化的情况 即我可以重写这个例子 在 python3 中 来使用super 例子取自文章super 被视为有害文章 https fuhm net s