¿Por qué no se pueden pasar * args y ** kwargs en __init__ de una clase de niño

Para entender * args y ** kwargs, realicé algunas búsquedas cuando me topé con esta pregunta * args y ** kwargs?

La respuesta debajo de la respuesta elegida me llamó la atención, que es esta:

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) 

Intenté algunas cosas en este ejemplo y ejecutando el código de esta manera:

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

Tengo esto:

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

Cambiar para ser como super(MyFoo, self).__init__(args, kwargs)

los resultados son:

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

Por alguna razón mental, estoy cuestionando esto: ¿qué podría ser correcto e incorrecto en el ejemplo anterior? ¿Qué se permitiría hacer y qué no en la producción de la vida real?

Su Foo.__init__() no admite argumentos de palabra clave arbitrarios. Puedes agregar **kw a su firma para que los acepte:

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

Los parámetros de palabra clave se comparan solo con argumentos con nombres de palabra clave coincidentes exactos; su método Foo necesitaría tener parámetros de palabras clave Python y stack . Si no se encuentra un parámetro de palabra clave coincidente pero un parámetro de **kw es , se recostackn en ese parámetro.

Si su subclase sabe que la clase padre solo tiene argumentos posicionales , siempre puede pasar los posicionales:

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

donde ahora debe pasar dos o más argumentos a MyFoo para que la llamada funcione.

En esencia, super().methodname devuelve una referencia al método enlazado; De ahí en adelante, es un método normal , por lo que debe pasar argumentos que cualquier método pueda aceptar. Si su método no acepta argumentos de palabras clave, obtendrá una excepción.

La razón es que todos los argumentos ya están desempaquetados en kwargs y ahora es un dict. y usted está tratando de pasarlo a una variable normal.

 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) 

Intenta hacer la modificación en

 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' 

Deberia trabajar..!

Cuando haces esto:

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

Es lo mismo que si hicieras esto, basándote en cómo funciona tu código:

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

Sin embargo, la función de inicio de Foo (de la que se hereda MyFoo no admite un argumento de palabra clave denominado “stack”).

Creo que vale la pena agregar que esto se puede usar para simplificar las firmas __init__ en las clases secundarias. Los argumentos posicionales se repiten de izquierda a derecha, por lo que si los agrega al frente y pasa el rest a args y kwargs, puede evitar cometer errores al agregarlos explícitamente a cada uno de los hijos. Hay alguna discusión acerca de si esa es una excepción aceptable “explícito es mejor que implícito” aquí . Para listas largas de argumentos en una jerarquía profunda, esto puede ser más claro y fácil de mantener.

Para modificar este ejemplo, agrego not_for_Foo al frente de MyFoo y paso el rest a través de 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