与基类__init__相比,派生类__init__中的参数更多 [英] More arguments in derived class __init__ than base class __init__
问题描述
如何扩展基类的__init__
,添加更多要解析的参数,而无需在每个派生类中都要求super().__init__(foo, bar)
?
How can I extend the __init__
of a base class, add more arguments to be parsed, without requiring super().__init__(foo, bar)
in every derived class?
class Ipsum:
""" A base ipsum instance. """
def __init__(self, foo, bar):
self.foo = flonk(foo)
grungole(self, bar)
self._baz = make_the_baz()
class LoremIpsum(Ipsum):
""" A more refined ipsum that also lorems. """
def __init__(self, foo, bar, dolor, sit, amet):
super().__init__(foo, bar)
farnark(sit, self, amet)
self.wibble(dolor)
该示例的目的是显示Ipsum.__init__
中正在发生大量处理,因此不应在每个子类中重复进行此处理.并且LoremIpsum.__init__
需要对foo
和bar
参数进行与Ipsum
完全相同的处理,但是还具有自己的特殊参数.
The purpose of the example is to show that there is significant processing happening in the Ipsum.__init__
, so it should not be duplicated in every sub-class; and the LoremIpsum.__init__
needs the foo
and bar
parameters processed exactly the same as Ipsum
, but also has its own special parameters.
它还表明,如果需要修改Ipsum
以接受其他签名,则每个派生类不仅需要更改其签名,还需要更改其调用超类__init__
的方式. .那太脆弱了.
It also shows that, if Ipsum
needs to be modified to accept a different signature, every derived class also needs to change not only its signature, but how it calls the superclass __init__
. That's unacceptably fragile.
相反,我想做类似的事情:
Instead, I'd like to do something like:
class Ipsum:
""" A base ipsum instance. """
def __init__(self, foo, bar, **kwargs):
self.foo = flonk(foo)
grungole(self, bar)
self._baz = make_the_baz()
self.parse_init_kwargs(kwargs)
def parse_init_kwargs(self, kwargs):
""" Parse the remaining kwargs to `__init__`. """
pass
class LoremIpsum(Ipsum):
""" A more refined ipsum that also lorems. """
def parse_init_kwargs(self, kwargs):
(dolor, sit, amet) = (kwargs['dolor'], kwargs['sit'], kwargs['amet'])
farnark(sit, self, amet)
self.wibble(dolor)
具有很大的优势,即LoremIpsum
只需要 做该类的特殊部分;处理Ipsum
参数由该类的__init__
处理,而无需任何额外的代码.
That has the big advantage that LoremIpsum
need only do the parts that are special to that class; handling Ipsum
arguments is handled by that class's __init__
without any extra code.
但是,缺点很明显:这通过传递字典来有效地重新实现对命名参数的处理.它避免了很多重复,但不是很清楚.
The disadvantage is obvious, though: this is effectively re-implementing the handling of named parameters by passing a dictionary around. It avoids a lot of repetition, but isn't very clear.
可以使用哪些工具来避免子类定义始终需要声明foo
和bar
参数,而始终则需要调用super().__init__(foo, bar)
?这些很容易出错,因此最好不要使用它们,并且可以自动发生,同时仍然允许LoremIpsum
定制初始化.
What tools are available to avoid the sub-class definitions always needing to declare the foo
and bar
parameters, and always needing to call super().__init__(foo, bar)
? Those are easy to get wrong, so it would be better if they weren't needed and could just automatically happen, while still allowing LoremIpsum
's customisation of the initialisation.
推荐答案
一种灵活的方法是,将祖先树中的每个方法共同设计为接受关键字参数和关键字参数字典,以删除所需的任何参数,并使用**kwds
转发其余的参数,最终将字典中的空字典留给链中的最后一次调用.
A flexible approach is to have every method in the ancestor tree cooperatively designed to accept keyword arguments and a keyword-arguments dictionary, to remove any arguments that it needs, and to forward the remaining arguments using **kwds
, eventually leaving the dictionary empty for the final call in the chain.
每个级别都剥离其所需的关键字参数,以便最终的空dict可以发送到根本不需要参数的方法(例如,object .__ init__希望参数为零):
Each level strips-off the keyword arguments that it needs so that the final empty dict can be sent to a method that expects no arguments at all (for example, object.__init__ expects zero arguments):
class Shape:
def __init__(self, shapename, **kwds):
self.shapename = shapename
super().__init__(**kwds)
class ColoredShape(Shape):
def __init__(self, color, **kwds):
self.color = color
super().__init__(**kwds)
cs = ColoredShape(color='red', shapename='circle')
有关此方法的更多信息,请参见 Pycon视频中查看相关文章.
For more on this approach, see the "Practical Advice" section of the Super Considered Super blogpost, or see the related at Pycon video.
在您的示例中,代码如下所示:
In your example, the code would look like this:
class Ipsum:
""" A base ipsum instance. """
def __init__(self, foo, bar):
self.foo = flonk(foo)
grungole(self, bar)
self._baz = make_the_baz()
class LoremIpsum(Ipsum):
""" A more refined ipsum that also lorems. """
def __init__(self, dolor, sit, amet, **kwds):
super().__init__(**kwds)
farnark(sit, self, amet)
self.wibble(dolor)
实例化如下:
li = LoremIpsum(dolor=1, sit=2, amet=3, foo=4, bar=5)
注意,这使您可以实现向__init__方法中添加新参数而不影响另一个参数的原始目标.
Note, this lets you achieve your original objective of adding new arguments to either __init__ method without affecting the other.
这篇关于与基类__init__相比,派生类__init__中的参数更多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!