【问题标题】:Why can't pass *args and **kwargs in __init__ of a child class为什么不能在子类的 __init__ 中传递 *args 和 **kwargs
【发布时间】:2014-02-09 15:15:57
【问题描述】:

为了理解 *args 和 **kwargs 我做了一些搜索,当我遇到这个问题时*args and **kwargs?

所选答案下方的答案引起了我的注意,是这样的:

class Foo(object):
    def __init__(self, value1, value2):
    # do something with the values
        print value1, value2

class MyFoo(Foo):
    def __init__(self, *args, **kwargs):
    # do something else, don't care about the args
        print 'myfoo'
        super(MyFoo, self).__init__(*args, **kwargs)

我在这个例子中尝试了一些东西并以这种方式运行代码:

class Foo(object):
    def __init__(self, value1, value2):
    # do something with the values
        print 'I think something is being called here'
        print value1, value2


class MyFoo(Foo):
    def __init__(self, *args, **kwargs):
    # do something else, don't care about the args
        print args, kwargs
        super(MyFoo, self).__init__(*args, **kwargs)


foo = MyFoo('Python', 2.7, stack='overflow')

我知道了:

[...]
    super(MyFoo, self).__init__(*args, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'stack'

变成super(MyFoo, self).__init__(args, kwargs)

结果是:

('Python', 2.7) {'stack': 'overflow'}
I think something is being called here
('Python', 2.7) {'stack': 'overflow'}

出于一些令人震惊的原因,我质疑这一点:在上面的示例中什么是对和错?在现实生活中什么可以做,什么不能做?

【问题讨论】:

    标签: python


    【解决方案1】:

    您的Foo.__init__() 确实支持任意关键字参数。您可以在其签名中添加**kw 以使其接受它们:

    class Foo(object):
        def __init__(self, value1, value2, **kw):
           print 'I think something is being called here'
           print value1, value2, kw
    

    关键字参数仅与关键字名称完全匹配的参数匹配;您的 Foo 方法需要有 Pythonstack 关键字参数。如果没有找到匹配的关键字参数,但找到了 **kw 参数,则它们将被收集在该参数中。

    如果你的子类知道父类只有位置参数,你总是可以传入位置:

    class MyFoo(Foo):
        def __init__(self, *args, **kwargs):
        # do something else, don't care about the args
            print args, kwargs
            while len(args) < 2:
                args += kwargs.popitem()
            super(MyFoo, self).__init__(*args[:2])
    

    您现在必须将两个或多个参数传递给MyFoo 以使调用生效。

    本质上,super().methodname 返回绑定方法的引用;从那里开始,它是一个 普通 方法,因此您需要传入 any 方法可以接受的参数。如果您的方法不接受关键字参数,则会出现异常。

    【讨论】:

    • @NiceGuy:是的,这是预期的输出。我不确定你的第二句话是什么意思。
    • 我明白了!我在第一条评论的最后一句话中模棱两可。
    • @MartijnPieters 是否可以拥有具有def __init__(self,*args,**kwargs): 类型构造函数的基类,然后在子类中继承此类,其中使用以下def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs)
    • @AlexanderCska:是的,这是可能的,但我不确定如果基类和子类都没有使用任何参数,那么让基类和子类都使用*args, **kwargs 的意义何在。跨度>
    • @MartijnPieters 我只粘贴了一小部分,以适应评论。对于给您带来的不便,我深表歉意。基本上我有 4 个参数(a、b、c、d)。基类使用self.a=a self.b=b,子类使用另外两个。问题是如果变量被隐式传递,python 找不到变量。 class base(object): def __init__(self,*args,**kwargs): self.a=a self.b=b class child(base): def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs) self.c=c self.d=d我不知道如何正确格式化。对不起
    【解决方案2】:

    当你这样做时:

    super(MyFoo, self).__init__(*args, **kwargs)
    

    这与您执行此操作相同,具体取决于您的代码的工作方式:

    super(MyFoo, self).__init__("python", 2.7, stack="overflow")
    

    但是,FooMyFoo 继承自该函数)的 __init__ 函数不支持名为“stack”的关键字参数。

    【讨论】:

    • 这是理解问题的又一击。谢谢! :)
    【解决方案3】:

    原因是所有参数都已经解压缩到 kwargs 中,现在它是一个 dict。并且您正在尝试将其传递给普通变量。

    def bun(args,kwargs):
    print 'i am here'
    print kwargs
    
    def fun(*args,**kwargs):
    print kwargs
    bun(*args,**kwargs)
    
     fun(hill=3,bi=9) # will fail.
    
    
    def bun(*args,**kwargs):
    print 'i am here'
    print kwargs
    
    def fun(*args,**kwargs):
    print kwargs
    bun(*args,**kwargs) # will work.
    
    
    
    fun(hill=3,bi=9)
    

    尝试修改

    class Foo(object):
        def __init__(self, *value1, **value2):
    # do something with the values
            print 'I think something is being called here'
            print value1, value2
    
    
    class MyFoo(Foo):
        def __init__(self, *args, **kwargs):
    # do something else, don't care about the args
            print args, kwargs
            super(MyFoo, self).__init__(*args, **kwargs)
    
    
    foo = MyFoo('Python', 2.7, stack='overflow'
    

    应该工作..!

    【讨论】:

      【解决方案4】:

      我认为值得补充的是,这可以用来简化子类中的 __init__ 签名。位置参数从左到右被删除,所以如果你将它们添加到前面并将其余的传递给 args 和 kwargs,你可以避免忘记将它们显式添加到每个孩子的错误。有一些关于这是否是可接受的例外“显式优于隐式”here 的讨论。对于深层层次结构中的长参数列表,这可能更清晰且更易于维护。

      为了修改这个例子,我将 not_for_Foo 添加到 MyFoo 的前面,并通过 super 传递其余部分。

      class Foo(object):
          def __init__(self, a_value1, a_value2, a_stack=None, *args, **kwargs):
              """do something with the values"""
              super(Foo, self).__init__(*args, **kwargs) # to objects constructor fwiw, but object.__init__() takes no args
              self.value1 = a_value1
              self.value2 = a_value2
              self.stack = a_stack
              return
      
          def __str__(self):
              return ', '.join(['%s: %s' % (k, v) for k, v in self.__dict__.items()])
      
      
      class MyFoo(Foo):
          def __init__(self, not_for_Foo, *args, **kwargs):
              # do something else, don't care about the args
              super(MyFoo, self).__init__(*args, **kwargs)
              self.not_for_Foo = not_for_Foo # peals off
              self.myvalue1 = 'my_' + self.value1 # already set by super
      
      
      if __name__ == '__main__':
      
          print 'Foo with args'
          foo = Foo('Python', 2.7, 'my stack')
          print foo
      
          print '\nMyFoo with kwargs'
          myfoo = MyFoo('my not for foo', value2=2.7, value1='Python', stack='my other stack')
          print myfoo
      
      
      $ python argsNkwargs.py 
      Foo with args
      value2: 2.7, value1: Python, stack: my stack
      
      MyFoo with kwargs
      myvalue1: my_Python, not_for_Foo: my not for foo, value2: 2.7, value1: 
      Python, stack: my other stack
      

      -lrm

      【讨论】:

      • 很有帮助,因为它验证了我对如何添加需要传递默认 args 的新子类构造函数的假设,但不会通过作为 **kwarg 传递而干扰默认 arg 默认值,我的麻烦是子类仅限于在前面而不是后面添加 args,因为他们在调用 super 时想要修改的任何 args 都需要在 **kwargs 字典中显式替换,而不是在 *args 列表中。必须显式修改 kwargs,因为子类没有明显的方法知道父类中的默认值是什么?
      猜你喜欢
      • 1970-01-01
      • 2021-11-26
      • 2020-10-10
      • 1970-01-01
      • 2019-04-21
      • 2016-02-06
      • 1970-01-01
      • 1970-01-01
      • 2014-05-31
      相关资源
      最近更新 更多