opcionales - Forma correcta de usar** kwargs en Python
python 3.6*args (13)
¿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 los valores predeterminados, o hay uno? ¿Debo acceder a él como un diccionario? ¿Usar función get?
class ExampleClass:
def __init__(self, **kwargs):
self.val = kwargs[''val'']
self.val2 = kwargs.get(''val2'')
Una pregunta simple, pero en 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.
@AbhinavGupta y @Steef sugirieron usar update()
, lo que encontré 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 falso / no compatible? @VinaySajip señaló que se puede usar pop()
para procesar iterativamente la lista de argumentos. Entonces, cualquier argumento sobrante es espurio. Bonito.
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 ocurren en los valores predeterminados.
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)
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 utilizarán; de lo contrario, se utilizarán los valores predeterminados que se han establecido.
Dado que **kwargs
se utiliza cuando se desconoce el número de argumentos, ¿por qué no hacerlo?
class Exampleclass(object):
def __init__(self, **kwargs):
for k in kwargs.keys():
if k in [acceptable_keys_list]:
self.__setattr__(k, kwargs[k])
Mientras que la mayoría de las respuestas dicen 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 verdad. En el último caso, f
se puede llamar como f(23, 42)
, mientras que el primer caso solo acepta argumentos con nombre, sin llamadas posicionales. A menudo, se desea permitir a la persona que llama la máxima flexibilidad y, por lo tanto, la segunda forma, como la mayoría de las respuestas afirma, es preferible: pero ese no es siempre el caso. Cuando acepta muchos parámetros opcionales de los cuales, por lo general, solo se pasan unos pocos, puede ser una excelente idea (¡evitar accidentes y códigos ilegibles en sus sitios de llamadas!) Para forzar el uso de argumentos con nombre - threading.Thread
. threading.Thread
es un ejemplo. La primera forma es cómo implementas eso en Python 2.
El idioma es tan importante que en Python 3 ahora tiene una sintaxis de soporte especial: cada argumento después de un solo *
en la firma de def
es solo de palabra clave, es decir, no se puede pasar como un argumento posicional, sino solo como uno nombrado. Así que en Python 3 podrías codificar lo anterior como:
def f(*, foo=None, bar=None, **kwargs):
...etc...
De hecho, en Python 3 puede incluso tener argumentos de palabra clave que no son opcionales (unos sin un valor predeterminado).
Sin embargo, Python 2 aún tiene muchos años de vida productiva por delante, por lo que es mejor no olvidar las técnicas y los modismos que le permiten implementar en Python 2 ideas de diseño importantes que se admiten directamente en el lenguaje en Python 3.
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()
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'']
Puede pasar un valor predeterminado para 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 en particular, ¿por qué no usar argumentos con nombre en primer lugar?
def __init__(self, val2="default value", **kwargs):
Si desea combinar esto con * args, debe mantener * args y ** kwargs al final de la definición.
Asi que:
def method(foo, bar=None, *args, **kwargs):
do_something_with(foo, bar)
some_other_function(*args, **kwargs)
Siguiendo la sugerencia de setattr 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
.
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 usa los valores de la forma que quieras
dictionaryA.update(dictionaryB)
agrega los contenidos de dictionaryB
al dictionaryA
sobrescribiendo cualquier clave duplicada.
Tu harias
self.attribute = kwargs.pop(''name'', default_value)
o
self.attribute = kwargs.get(''name'', default_value)
Si usa pop
, entonces puede verificar si se han enviado valores falsos y tomar la acción apropiada (si corresponde).
Usar ** kwargs y los valores por defecto es fácil. A veces, sin embargo, no deberías 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 está utilizando ** kwargs, quiere decir que una palabra clave no es solo opcional, sino condicional. Hay reglas más complejas que simples valores por defecto.
Cuando estás usando ** kwargs, por lo general te refieres a algo más parecido a lo siguiente, donde no se aplican valores predeterminados simples.
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" )