Why can't pass *args and **kwargs in __init__ of a child class

后端 未结 4 508
情话喂你
情话喂你 2020-12-09 09:56

To understand *args and **kwargs I made some searchs about, when I fell on this question *args and **kwargs?

The answer below the chosen answer caught my attention,

相关标签:
4条回答
  • 2020-12-09 10:21

    Your Foo.__init__() does not support arbitrary keyword arguments. You can add **kw to it's signature to make it accept them:

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

    Keyword parameters are matched only with arguments with exact matching keyword names; your Foo method would need to have Python and stack keyword parameters. If no matching keyword parameter are found but a **kw parameter is, they are collected in that parameter instead.

    If your subclass knows that the parent class only has positional arguments, you can always pass in positionals:

    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])
    

    where you now must pass in two or more arguments to MyFoo for the call to work.

    In essence, super().methodname returns a reference to the bound method; from there on out it is a normal method, so you need to pass in arguments that any method can accept. If your method doesn't accept keyword arguments, you get an exception.

    0 讨论(0)
  • 2020-12-09 10:24

    I think it is worth adding that this can be used to simplify the __init__ signatures in the child classes. The positional arguments are pealed off from left to right so if you add them to the front and pass the rest to args and kwargs you can avoid mistakes from forgetting to add them explicitly to each of the children. There is some discussion about if that is an acceptable exception "explicit is better than implicit" here. For long lists of args in deep hierarchy this may be clearer and easier to maintain.

    To modify this example, I add not_for_Foo to the front of MyFoo and pass the rest through 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

    0 讨论(0)
  • 2020-12-09 10:27

    When you do this:

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

    It is the same as if you did this, base on how your code is working:

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

    However, the __init__ function of Foo (from which MyFoo inherits) doesn't support a keyword argument named "stack".

    0 讨论(0)
  • 2020-12-09 10:28

    The reason is all the arguments are already unpacked into kwargs and it is a dict now. and you are trying to pass it to a normal variables.

    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)
    

    Try making the modification at

    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'
    

    should work..!

    0 讨论(0)
提交回复
热议问题