python继承super__init___python – 当直接从`object`继承时,我应该调用super().__ init __()吗?...

在Python中,是否应该在直接继承自`object`的类中调用`super().__init__() `取决于你的需求。通常,调用`super().__init__() `用于协作继承,确保参数被正确处理。`object.__init__`不接受参数,它的存在是为了确保无参数调用。理解MRO(方法解析顺序)和C3线性化算法对于决定何时使用`super()`至关重要。在某些情况下,你可能需要通过条件调用来适应特定的继承结构。
摘要由CSDN通过智能技术生成

Should classes that inherit directly from object call super().__init__()?

你好像在搜索这个问题的一个简单的“是或否”答案,但不幸的是答案是“它取决于”.此外,在决定是否应该调用super().__ init __()时,类是否直接从object继承是有点无关紧要的.不变的是,如果调用object .__ init__,则应该不带参数调用它 – 因为object .__ init__不接受参数.

实际上,在协作继承情况下,这意味着必须确保在调用object .__ init__之前消耗所有参数.这并不意味着你应该尽量避免调用object .__ init__. Here是在调用super之前消耗args的示例,响应和请求上下文已从可变映射kwargs中弹出.

我之前提到过,一个类是否直接从object继承是一个红色的herring1.但是我没有提到应该激发这个设计决定的内容:如果你想让MRO继续搜索其他初始化器,你应该调用super init [read:super anymethod] [read:other anymethods].如果要指示应在此处停止MRO搜索,则不应调用super.

为什么对象.__ init__完全存在,如果它什么都不做?因为它确实做了一些事情:确保它不带参数调用.参数的存在可能表明存在错误2.对象也用于停止超级调用链的目的 – 有人不得调用超级,否则我们无限地递归.你可以通过不调用super来自己明确地停止它.如果不这样做,对象将作为最终链接并为您停止链接.

类MRO在编译时确定,通常在定义类时/导入模块时.但是,请注意,super的使用涉及运行时分支的许多机会.你必须考虑:

>调用super的方法的哪些参数(即,您希望沿MRO转发哪些参数)

>是否完全调用super(有时你想故意破坏链)

>创建超级本身的参数(如果有的话)(有一个高级用例,如下所述)

>是否在自己的方法中在代码之前或之后调用代理方法(如果需要访问代理方法设置的某些状态,则先放置超级方法,如果您设置代理方法所依赖的某种状态,则将超级方法置于最后已经 – 在某些情况下你甚至想把超级中间放在中间!)

>问题主要是关于__init__,但不要忘记super也可以用于任何其他方法

在极少数情况下,您可以有条件地调用超级呼叫.您可以检查您的super()实例是否具有此属性或该属性,并在结果周围建立一些逻辑.或者,您可以调用super(OtherClass,self)来显式“跳过”链接并手动遍历此部分的MRO.是的,如果默认行为不是你想要的,你可以劫持MRO!所有这些恶魔般的想法的共同点是对C3 linearization算法的理解,Python如何制作MRO,以及超级本身如何使用MRO. Python的实现或多或少从another programming language开始提升,其中super被命名为next-method.老实说,super在Python中是一个超级糟糕的名字,因为它会在初学者中引起一个常见的误解,即你总是调用“up”到其中一个父类,我希望他们选择了一个更好的名字.

在定义继承层次结构时,解释器无法知道您是否要重用其他类的现有功能,或者将其替换为替代实现或其他内容.任何一个决定都可能是一个有效和实用的设计.如果有一个关于何时以及如何调用super的强硬规则,那么程序员就不会选择 – 语言会从你手中做出决定并自动做正确的事情.我希望能充分解释在__init__中调用super不是一个简单的是/否问题.

If yes, how would you correctly initialize SuperFoo?

(F00,SuperFoo等来源于问题this revision)

为了回答这一部分,我将假设MCVE中显示的__init__方法实际上需要进行一些初始化(也许你可以在问题的MCVE代码中添加占位符注释).如果你所做的只是使用相同的参数调用super,那么根本不要定义__init__,这没有意义.不要定义只是通过的__init__,除非你故意意味着停止那里的MRO遍历(在这种情况下,评论肯定是正当的!).

首先,在我们讨论SuperFoo之前​​,让我说NoSuperFoo看起来像一个不完整或糟糕的设计.你如何将foo参数传递给Foo的init? foo初始值为3是硬编码的.可能可以硬编码(或以其他方式自动确定)foo的初始值,但随后是you should probably be doing composition not inheritance.

至于SuperFoo,它继承了SuperCls和Foo. SuperCls看起来是为了继承,Foo不是.这意味着你可能有一些工作要做,正如super harmful所指出的那样.前进的方法as discussed in Raymond’s blog正在编写适配器.

class FooAdapter:

def __init__(self, **kwargs):

foo_arg = kwargs.pop('foo')

# can also use kwargs['foo'] if you want to leave the responsibility to remove 'foo' to someone else

# can also use kwargs.pop('foo', 'foo-default') if you want to make this an optional argument

# can also use kwargs.get('foo', 'foo-default') if you want both of the above

self._the_foo_instance = Foo(foo_arg)

super().__init__(**kwargs)

# add any methods, wrappers, or attribute access you need

@property

def foo():

# or however you choose to expose Foo functionality via the adapter

return self._the_foo_instance.foo

请注意,FooAdapter有一个Foo,而不是FooAdapter是一个Foo.这不是唯一可能的设计选择.但是,如果你继承类FooParent(Foo),那么你暗示FooParent是一个Foo,并且可以在Foo可能使用的任何地方使用 – 通过使用合成来避免违反LSP通常更容易. SuperCls还应合作允许** kwargs:

class SuperCls:

def __init__(self, **kwargs):

# some other init code here

super().__init__(**kwargs)

也许SuperCls也不受你的控制,你也必须适应它,所以就这样吧.关键是,这是一种重用代码的方法,通过调整接口使签名匹配.假设每个人都在合作并且消耗他们需要的东西,最终super().__ init __(** kwargs)将代理到object .__ init __(** {}).

Since 99% of classes I’ve seen don’t use **kwargs in their constructor, does that mean 99% of python classes are implemented incorrectly?

不,因为YAGNI.在它们有用之前,99%的班级需要立即支持100%普通依赖注入所有的花里胡哨吗?如果他们没有,他们会被打破吗?例如,请考虑集合文档中给出的OrderedCounter配方. Counter.__init__接受* args和** kwargs,但是doesn’t proxy them in the super init call.如果你想使用其中一个参数,那么运气好,你必须覆盖__init__并拦截它们. OrderedDict根本没有合作定义,实际上,某些parent calls are hardcoded是dict – 并且没有调用下一行的__init__,所以任何MRO遍历都会在那里停止.如果您不小心将其定义为OrderedCounter(OrderedDict,Counter)而不是OrderedCounter(Counter,OrderedDict),则元类库仍然可以创建一致的MRO,但该类根本不能作为有序计数器工作.

尽管存在所有这些缺点,OrderedCounter配方的工作方式与广告一样,因为MRO是按照预期用例设计的.因此,您甚至不需要100%正确地进行协作继承以实现依赖注入.故事的寓意是完美是进步的敌人(或者,practicality beats purity).如果你想将MyWhateverClass塞进任何疯狂的继承树,你可以梦想,继续,但是你需要编写必要的脚手架来实现这一目标.像往常一样,Python不会阻止你以任何hacky方式实现它,并且足够好用.

1无论是否在类声明中写入,都始终从对象继承.许多开源代码库无论如何都会明确地从对象继承,以便与2.7运行时交叉兼容.

2这一点在CPython来源here中更详细地解释了__new__和__init__之间的微妙关系.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值