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