Forma correcta de usar * * kwargs en Python
¿Cuál es la forma correcta de usar **kwargs
en Python cuando se trata de valores predeterminados?
kwargs
devuelve un diccionario, pero ¿cuál es la mejor manera de establecer valores predeterminados, o hay uno? ¿Debería acceder a él como un diccionario? ¿Usar la función get?
class ExampleClass:
def __init__(self, **kwargs):
self.val = kwargs['val']
self.val2 = kwargs.get('val2')
Una pregunta simple, pero sobre la que no puedo encontrar buenos recursos. La gente lo hace de diferentes maneras en el código que he visto y es difícil saber qué usar.
13 answers
Puede pasar un valor predeterminado a get()
para las claves que no están en el diccionario:
self.val2 = kwargs.get('val2',"default value")
Sin embargo, si planea usar un argumento en particular con un valor predeterminado particular, ¿por qué no usar argumentos con nombre en primer lugar?
def __init__(self, val2="default value", **kwargs):
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 14:57:23
Mientras que la mayoría de las respuestas están diciendo que, por ejemplo,
def f(**kwargs):
foo = kwargs.pop('foo')
bar = kwargs.pop('bar')
...etc...
Es"lo mismo que"
def f(foo=None, bar=None, **kwargs):
...etc...
Esto no es cierto. En este último caso, f
puede ser llamado como f(23, 42)
, mientras que el primer caso acepta argumentos con nombre solo no sin llamadas posicionales. A menudo se desea permitir al llamante la máxima flexibilidad y, por lo tanto, la segunda forma, como la mayoría de las respuestas afirman, es preferible: pero ese no es siempre el caso. Cuando acepta muchos parámetros opcionales, de los cuales normalmente solo unos pocos son pasado, puede ser una excelente idea (evitar accidentes y código ilegible en sus sitios de llamadas!) para forzar el uso de argumentos con nombre {threading.Thread
es un ejemplo. El primer formulario es cómo implementarlo en Python 2.
El modismo es tan importante que en Python 3 ahora tiene una sintaxis de soporte especial: cada argumento después de una única *
en la firma def
es solo una palabra clave, es decir, no se puede pasar como un argumento posicional, sino solo como uno con nombre. Así que en Python 3 podrías codificar lo anterior as:
def f(*, foo=None, bar=None, **kwargs):
...etc...
De hecho, en Python 3 incluso puede tener argumentos solo para palabras clave que no son opcionales (sin un valor predeterminado).
Sin embargo, Python 2 todavía tiene largos años de vida productiva por delante, por lo que es mejor no olvidar las técnicas y expresiones que le permiten implementar en Python 2 ideas de diseño importantes que son compatibles directamente con el lenguaje en Python 3!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 15:29:29
Sugiero algo como esto
def testFunc( **kwargs ):
options = {
'option1' : 'default_value1',
'option2' : 'default_value2',
'option3' : 'default_value3', }
options.update(kwargs)
print options
testFunc( option1='new_value1', option3='new_value3' )
# {'option2': 'default_value2', 'option3': 'new_value3', 'option1': 'new_value1'}
testFunc( option2='new_value2' )
# {'option1': 'default_value1', 'option3': 'default_value3', 'option2': 'new_value2'}
Y luego use los valores de la manera que desee
dictionaryA.update(dictionaryB)
añade el contenido de dictionaryB
a dictionaryA
sobrescribiendo cualquier clave duplicada.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 15:10:34
Usted haría
self.attribute = kwargs.pop('name', default_value)
O
self.attribute = kwargs.get('name', default_value)
Si usa pop
, entonces puede verificar si hay algún valor falso enviado, y tomar la acción apropiada (si la hay).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 14:49:58
Usar **kwargs y valores predeterminados es fácil. A veces, sin embargo, no debería usar **kwargs en primer lugar.
En este caso, realmente no estamos haciendo el mejor uso de **kwargs.
class ExampleClass( object ):
def __init__(self, **kwargs):
self.val = kwargs.get('val',"default1")
self.val2 = kwargs.get('val2',"default2")
Lo anterior es un "¿por qué molestarse?" declaración. Es lo mismo que
class ExampleClass( object ):
def __init__(self, val="default1", val2="default2"):
self.val = val
self.val2 = val2
Cuando usas **kwargs, quieres decir que una palabra clave no es solo opcional, sino condicional. Hay reglas más complejas que simples valores predeterminados.
Cuando estás usando * * kwargs, generalmente quieres decir algo más como lo siguiente, donde los valores predeterminados simples no se aplican.
class ExampleClass( object ):
def __init__(self, **kwargs):
self.val = "default1"
self.val2 = "default2"
if "val" in kwargs:
self.val = kwargs["val"]
self.val2 = 2*self.val
elif "val2" in kwargs:
self.val2 = kwargs["val2"]
self.val = self.val2 / 2
else:
raise TypeError( "must provide val= or val2= parameter values" )
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 14:55:22
Dado que **kwargs
se usa cuando el número de argumentos es desconocido, ¿por qué no hacer esto?
class Exampleclass(object):
def __init__(self, **kwargs):
for k in kwargs.keys():
if k in [acceptable_keys_list]:
self.__setattr__(k, kwargs[k])
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-29 16:15:24
Aquí hay otro enfoque:
def my_func(arg1, arg2, arg3):
... so something ...
kwargs = {'arg1': 'Value One', 'arg2': 'Value Two', 'arg3': 'Value Three'}
# Now you can call the function with kwargs like this:
my_func(**kwargs)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-13 00:44:27
Creo que la forma correcta de usar **kwargs
en Python cuando se trata de valores predeterminados es usar el método de diccionario setdefault
, como se indica a continuación:
class ExampleClass:
def __init__(self, **kwargs):
kwargs.setdefault('val', value1)
kwargs.setdefault('val2', value2)
De esta manera, si un usuario pasa 'val' o 'val2' en la palabra clave args
, se usarán; de lo contrario, se usarán los valores predeterminados que se han establecido.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-22 20:46:15
Podrías hacer algo como esto
class ExampleClass:
def __init__(self, **kwargs):
arguments = {'val':1, 'val2':2}
arguments.update(kwargs)
self.val = arguments['val']
self.val2 = arguments['val2']
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 14:51:57
Siguiendo la sugerencia de @srhegde de usar setattr:
class ExampleClass(object):
__acceptable_keys_list = ['foo', 'bar']
def __init__(self, **kwargs):
[self.__setattr__(key, kwargs.get(key)) for key in self.__acceptable_keys_list]
Esta variante es útil cuando se espera que la clase tenga todos los elementos en nuestra lista acceptable
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:57
Si desea combinar esto con *args usted tiene que mantener *args y **kwargs al final de la definición.
Así que:
def method(foo, bar=None, *args, **kwargs):
do_something_with(foo, bar)
some_other_function(*args, **kwargs)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-19 09:50:37
@AbhinavGupta y @ Steef sugirieron usar update()
, lo cual me pareció muy útil para procesar grandes listas de argumentos:
args.update(kwargs)
¿Qué pasa si queremos comprobar que el usuario no ha pasado ningún argumento espurio/no soportado? @VinaySajip señaló que pop()
se puede usar para procesar iterativamente la lista de argumentos. Entonces, cualquier argumento sobrante es falso. Agradable.
Aquí hay otra forma posible de hacer esto, que mantiene la sintaxis simple de usar update()
:
# kwargs = dictionary of user-supplied arguments
# args = dictionary containing default arguments
# Check that user hasn't given spurious arguments
unknown_args = user_args.keys() - default_args.keys()
if unknown_args:
raise TypeError('Unknown arguments: {}'.format(unknown_args))
# Update args to contain user-supplied arguments
args.update(kwargs)
unknown_args
es un set
contiene los nombres de los argumentos que no aparecen en los valores predeterminados.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-22 02:08:18
Otra solución simple para procesar argumentos desconocidos o múltiples puede ser:
class ExampleClass(object):
def __init__(self, x, y, **kwargs):
self.x = x
self.y = y
self.attributes = kwargs
def SomeFunction(self):
if 'something' in self.attributes:
dosomething()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-25 13:40:36