Почему не удается передать * args и ** kwargs в __init__ дочернего класса
Чтобы понять * args и ** kwargs, я сделал несколько поисков, когда я упал на этот вопрос * args и ** 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'}
Для некоторых причин разума я спрашиваю: что может быть правильным и неправильным в приведенном выше примере? Что можно было бы сделать и что бы не было в реальной жизни?
Ответы
Ответ 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
должен иметь параметры ключевого слова Python
и stack
. Если не найдено подходящего параметра ключевого слова, а параметр **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
возвращает ссылку на связанный метод; оттуда это обычный метод, поэтому вам нужно передать аргументы, которые может принять любой метод. Если ваш метод не принимает аргументы ключевого слова, вы получаете исключение.
Ответ 2
Когда вы делаете это:
super(MyFoo, self).__init__(*args, **kwargs)
Это так же, как если бы вы это сделали, основываясь на том, как работает ваш код:
super(MyFoo, self).__init__("python", 2.7, stack="overflow")
Однако функция __init__
в Foo
(от которой наследуется MyFoo
) не поддерживает аргумент ключевого слова с именем "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__ в дочерних классах. Позиционные аргументы отбрасываются слева направо, поэтому, если вы добавляете их на передний план и передаете остальные аргументы и kwargs, вы можете избежать ошибок от забывания, чтобы добавить их явно каждому из этих детей. Существует некоторая дискуссия о том, является ли это приемлемым исключением, "явное лучше, чем неявное" здесь. Для длинных списков аргументов в глубокой иерархии это может быть яснее и проще в обслуживании.
Чтобы изменить этот пример, я добавляю 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