【问题标题】:inheritance in python: correct use of __init__ and super() in real lifepython中的继承:在现实生活中正确使用__init__和super()
【发布时间】:2019-12-12 02:40:56
【问题描述】:

我正在与一个反复出现的噩梦作斗争,即不知道在一个小的类层次结构中管理__init__ 的参数的最佳方法是什么。我已经阅读了几篇关于使用super() 有多好的问答和文章,我理解它,但是许多示例只是处理__init__ 的基本情况而没有参数,这很少是我的情况。

举个例子,我有一个类root,属性为tagsdescription,它看起来像:

class Root(object):
    """An object that can be tagged."""
    def __init__(tags = None, description = None):
        self.tags, self.description = tags, description

现在我可以子类化了,但是 tagsdescription 的参数呢?我可以在子类中明确接受它们:

class Derived(Root):
    def __init__(self,tags = None, description = None):
        super().__init__(tags = tags, description = description)

或者我可以通过*args, **kwargs传递它们

如果我决定,第一个解决方案迫使我为所有派生类重写例程描述符(以及文档和文档字符串),例如在Root 中添加或删除参数。第二个自动工作,但它隐藏了自省的参数名称;如果任何其他例程需要接受来自*args, **kwargs 的可选参数,则需要手动过滤。

第三种方法可能是显式地重新分配子类中的属性,而不调用super.__init__,但这对我来说看起来是重复的代码,所以它可能不太好。 处理这种情况的最佳方法是什么?

【问题讨论】:

    标签: python python-3.x inheritance multiple-inheritance


    【解决方案1】:

    首先:super 是专门为支持多重继承而创建的。使用它时,您不应假设仅通过查看 your 类的基类就可以预测 self 是哪些类的实例。


    您应该使用*kwargs,但不仅仅是传递tagsdescription。相反,它用于接受 MRO 中的某些 other 类可能需要的任意关键字参数,因此您也可以传递它们。如果操作正确,那么在调用object.__init__kwargs 将为空。

    class Root(object):
        """An object that can be tagged."""
        def __init__(self, tags=None, description=None, **kwargs):
            # If object is next, kwargs should be empty
            # Otherwise, it has arguments that Root doesn't know or care
            # about, but some other class does.
            super().__init__(**kwargs)
            self.tags, self.description = tags, description
    
    
    class Derived(Root):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            # Other stuff here
    
    
    d = Derived(tags="hello", description="world")
    

    注意,如果Derived.__init__ 什么都不做除了调用super().__init__,你根本不需要定义它。

    __init__ 的所有调用都应使用关键字参数来完成,以避免在谁获得什么位置参数方面发生任何潜在冲突。


    一个例子:

    class SomethingElse:
        def __init__(self, color, **kwargs):
            super().__init__(**kwargs)
            self.color = color
    
    class Foo(Derived, SomethingElse):
        def __init__(self, size, **kwargs):
            super().__init__(**kwargs)
            self.size = size
    
    
    f = Foo(size="large", tags="loud,noisy", color="red", description="strange")
    

    Foo.__init__ 首先被调用。它识别size 并将其余参数留在kwargs 中。接下来是Derived.__init__;它只是将所有内容传递给下一个方法,即Root.__init__。它识别tagsdescription,并留下color 以传递到下一个方法SomethingElse.__init__。该方法识别color,并将其他任何内容传递给下一个,在本例中为最终方法object.__init__。因为每个类都小心翼翼地删除了它引入的参数,所以此时kwargs 是空的,因此没有任何东西传递给object.__init__,正如它所期望的那样。

    【讨论】:

      【解决方案2】:

      我建议使用*args 后跟**kwargs 以允许该类的用户根据需要以位置方式传递标签和描述。它可能会使您的构造函数变得更加复杂,因为逻辑要确定哪个包含超类构造函数的每个参数,但它会为您和其他开发人员将来的使用提供更好的体验。

      class Root(object):
          """An object that can be tagged."""
          def __init__(self, tags = None, description = None):
              self.tags, self.description = tags, description
      
      class Derived(Root):
          def __init__ (self, *args, *kwargs):
              if len(args) >= 2:
                  # Both args to Root.__init__ were passed as positional arguments
                  super().__init__(*args)
      
              elif len(args) == 1:
                  # One of the arguments was passed as a named argument.
                  super().__init__(*args, kwargs["description"])
      
              else:
                  # Both arguments passed as named args.
                  super().__init__(**kwargs)
      
              ####### Other stuff here #######
      

      当然,这需要更多的工作,但是当您编写的构造函数不会强制您将所有参数作为命名参数传递时,您会更开心。

      【讨论】:

      • 一旦开始使用多重继承,使用*args 可能会遇到问题,因为您不一定知道哪个方法会尝试使用哪个位置参数。
      • 确实如此。一个人可以遵守一个约定,比如位置参数总是去超类,但是如果他们不知道这是第一次查看某些代码时的约定,或者它是旧代码,那么应该怎么做......这确实是一个具有挑战性的问题。
      • super 的全部意义在于支持没有“the”超类概念的情况。如果 Python 不支持多重继承,它根本不会费心引入superRoot.__init__(self, *args, **kwargs) 就足够了,您可以更容易地要求子类在其单亲之后引入新参数。
      猜你喜欢
      • 2020-07-05
      • 2020-03-13
      • 2021-11-28
      • 2018-11-06
      • 2021-11-23
      • 1970-01-01
      • 2016-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多