Muchas respuestas tienen pequeños fragmentos de la información completa, así que me gustaría reunirlo todo junto con mis patrones favoritos.
el valor predeterminado es un mutable
tipo
Si el valor predeterminado es un objeto mutable, tiene suerte: puede aprovechar el hecho de que los argumentos predeterminados de Python se evalúan una vez cuando se define la función (algo más sobre esto al final de la respuesta en la última sección)
Esto significa que puede comparar fácilmente un valor mutable predeterminado usando is
para ver si se pasó como un argumento o se dejó por defecto, como en los siguientes ejemplos como función o método:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
y
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Argumentos predeterminados inmutables
Ahora, es un poco menos elegante si se espera que su valor predeterminado sea un immutable
valor (¡y recuerde que incluso las cadenas son inmutables!) Porque no puede explotar el truco tal como está, pero todavía hay algo que puede hacer, aún explotando mutable tipo; Básicamente, pones un valor predeterminado "falso" mutable en la firma de la función y el valor predeterminado "real" deseado en el cuerpo de la función.
def f(value={}):
"""
my function
:param value: value for my function; default is 1
"""
if value is f.__defaults__[0]:
print('default')
value = 1
else:
print('passed in the call')
print(value)
Se siente particularmente divertido si su valor predeterminado real es None
, pero None
es inmutable, por lo que ... aún necesita usar explícitamente un mutable como parámetro predeterminado de la función y cambiar a Ninguno en el código.
Usando una Default
clase para valores predeterminados inmutables
o, similar a la sugerencia de @cz, si los documentos de Python no son suficientes :-), puede agregar un objeto en el medio para hacer la API más explícita (sin leer los documentos); la instancia de la clase used_proxy_ Default es mutable y contendrá el valor predeterminado real que desea usar.
class Default:
def __repr__(self):
return "Default Value: {} ({})".format(self.value, type(self.value))
def __init__(self, value):
self.value = value
def f(default=Default(1)):
if default is f.__defaults__[0]:
print('default')
print(default)
default = default.value
else:
print('passed in the call')
print("argument is: {}".format(default))
ahora:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Lo anterior también funciona muy bien para Default(None)
.
Otros patrones
Obviamente, los patrones anteriores se ven más feos de lo que deberían debido a todos los print
cuales están ahí solo para mostrar cómo funcionan. De lo contrario, los encuentro concisos y lo suficientemente repetibles.
Podría escribir un decorador para agregar el __call__
patrón sugerido por @dmg de una manera más simplificada, pero esto aún obligará a usar trucos extraños en la definición de la función en sí; necesitaría dividirse value
y value_default
si su código necesita distinguirlos, entonces No veo mucha ventaja y no escribiré el ejemplo :-)
Tipos mutables como valores predeterminados en Python
¡Un poco más sobre el número 1 de Python! , abusado para su propio placer arriba. Puede ver lo que sucede debido a la evaluación en la definición haciendo:
def testme(default=[]):
print(id(default))
Puede ejecutar testme()
tantas veces como desee, siempre verá una referencia a la misma instancia predeterminada (por lo que básicamente su valor predeterminado es inmutable :-)).
Recuerde que en Python sólo hay 3 mutable tipos incorporados : set
, list
, dict
; todo lo demás, ¡incluso las cuerdas! - es inmutable.