funciones - argumentos opcionales python
¿Por qué usar** kwargs en python? ¿Cuáles son algunas ventajas del mundo real sobre el uso de argumentos con nombre? (8)
Vengo de un fondo en lenguajes estáticos. ¿Puede alguien explicar (idealmente a través del ejemplo) las ventajas del mundo real de usar ** kwargs con argumentos nombrados ?
Para mí, solo parece hacer que la función llame más ambigua. Gracias.
Aquí hay un ejemplo, lo usé en CGI Python. **kwargs
una clase que llevó **kwargs
a la función __init__
. Eso me permitió emular el DOM en el lado del servidor con clases:
document = Document()
document.add_stylesheet(''style.css'')
document.append(Div(H1(''Imagist/'s Page Title''), id = ''header''))
document.append(Div(id=''body''))
El único problema es que no puede hacer lo siguiente, porque la class
es una palabra clave de Python.
Div(class = ''foo'')
La solución es acceder al diccionario subyacente.
Div(**{''class'':''foo''})
No estoy diciendo que este sea un uso "correcto" de la función. Lo que estoy diciendo es que hay todo tipo de formas imprevistas en las que se pueden usar funciones como esta.
Ejemplos del mundo real:
Decoradores: generalmente son genéricos, por lo que no puede especificar los argumentos por adelantado:
def decorator(old):
def new(*args, **kwargs):
# ...
return old(*args, **kwargs)
return new
Lugares donde desea hacer magia con una cantidad desconocida de argumentos de palabra clave. El ORM de Django hace eso, por ejemplo:
Model.objects.filter(foo__lt = 4, bar__iexact = ''bar'')
Es posible que desee aceptar argumentos con nombres casi arbitrarios por una serie de razones, y eso es lo que el formulario **kw
permite hacer.
La razón más común es pasar los argumentos directamente a alguna otra función que esté envolviendo (¡los decoradores son un caso de esto, pero LEJOS de la única!) - en este caso, **kw
afloja el acoplamiento entre envoltura y wrappee, ya que el envoltorio no tiene que saber o preocuparse por todos los argumentos de la víctima. Aquí hay otra razón completamente diferente:
d = dict(a=1, b=2, c=3, d=4)
si todos los nombres deben conocerse con antelación, entonces obviamente este enfoque simplemente no podría existir, ¿verdad? Y, por cierto, cuando corresponda, prefiero esta manera de hacer un dict cuyas claves sean cadenas literales para:
d = {''a'': 1, ''b'': 2, ''c'': 3, ''d'': 4}
simplemente porque este último es bastante puntuado y, por lo tanto, menos legible.
Cuando ninguna de las excelentes razones para aceptar **kwargs
aplica, entonces no la acepte: es así de simple. IOW, si no hay una buena razón para permitir que la persona que llama pase argumentos adicionales con nombres arbitrarios, no permita que eso suceda; simplemente evite poner un formulario **kw
al final de la firma de la función en la declaración de def
.
En cuanto a usar **kw
en una llamada, eso le permite juntar el conjunto exacto de argumentos con nombre que debe pasar, cada uno con los valores correspondientes, en un dict, independientemente de un único punto de llamada, y luego usar ese dict en la sola llamada punto. Comparar:
if x: kw[''x''] = x
if y: kw[''y''] = y
f(**kw)
a:
if x:
if y:
f(x=x, y=y)
else:
f(x=x)
else:
if y:
f(y=y)
else:
f()
Incluso con solo dos posibilidades (¡y del tipo más simple!), La falta de **kw
está convirtiendo la segunda opción en absolutamente insostenible e intolerable. Solo imagine cómo se desarrolla cuando hay media docena de posibilidades, posiblemente en un poco más rico. interacción ... sin **kw
, ¡la vida sería un infierno absoluto en tales circunstancias!
Hay dos casos comunes:
Primero: está ajustando otra función que toma un número de argumento de palabra clave, pero usted simplemente los va a pasar:
def my_wrapper(a, b, **kwargs):
do_something_first(a, b)
the_real_function(**kwargs)
Segundo: está dispuesto a aceptar cualquier argumento de palabra clave, por ejemplo, para establecer atributos en un objeto:
class OpenEndedObject:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
foo = OpenEndedObject(a=1, foo=''bar'')
assert foo.a == 1
assert foo.foo == ''bar''
Otra razón por la que podría querer usar **kwargs
(y *args
) es si está extendiendo un método existente en una subclase. Desea pasar todos los argumentos existentes al método de la superclase, pero quiere asegurarse de que su clase siga funcionando incluso si la firma cambia en una versión futura:
class MySubclass(Superclass):
def __init__(self, *args, **kwargs):
self.myvalue = kwargs.pop(''myvalue'', None)
super(MySubclass, self).__init__(*args, **kwargs)
Un ejemplo es implementar python-argument-binders , utilizado de esta manera:
>>> from functools import partial >>> def f(a, b): ... return a+b >>> p = partial(f, 1, 2) >>> p() 3 >>> p2 = partial(f, 1) >>> p2(7) 8
Esto es de los documentos de functools.partial python: partial es ''relativamente equivalente'' a este impl:
def partial(func, *args, **keywords): def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) return func(*(args + fargs), **newkeywords) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfunc
Y aquí hay otro ejemplo típico:
MESSAGE = "Lo and behold! A message {message!r} came from {object_} with data {data!r}."
def proclaim(object_, message, data):
print(MESSAGE.format(**locals()))
**kwargs
son buenos si no sabes de antemano el nombre de los parámetros. Por ejemplo, el constructor dict
los usa para inicializar las claves del nuevo diccionario.
dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
In [3]: dict(one=1, two=2)
Out[3]: {''one'': 1, ''two'': 2}