En respuesta a la pregunta de los buenos usos de los valores de argumento predeterminados mutables, ofrezco el siguiente ejemplo:
Un valor predeterminado mutable puede ser útil para programar comandos fáciles de usar e importables de su propia creación. El método predeterminado mutable equivale a tener variables estáticas privadas en una función que puede inicializar en la primera llamada (muy parecido a una clase) pero sin tener que recurrir a globales, sin tener que usar un contenedor y sin tener que instanciar un objeto de clase que se importó. Es elegante a su manera, como espero que estén de acuerdo.
Considere estos dos ejemplos:
def dittle(cache = []):
from time import sleep
if type(cache) != list or cache !=[] and (len(cache) == 2 and type(cache[1]) != int):
print(" User called dittle("+repr(cache)+").\n >> Warning: dittle() takes no arguments, so this call is ignored.\n")
return
if not cache:
print("\n cache =",cache)
print(" Initializing private mutable static cache. Runs only on First Call!")
cache.append("Hello World!")
cache.append(0)
print(" cache =",cache,end="\n\n")
cache[1]+=1
outstr = " dittle() called "+str(cache[1])+" times."
if cache[1] == 1:outstr=outstr.replace("s.",".")
print(outstr)
print(" Internal cache held string = '"+cache[0]+"'")
print()
if cache[1] == 3:
print(" Let's rest for a moment.")
sleep(2.0)
print(" Wheew! Ready to continue.\n")
sleep(1.0)
elif cache[1] == 4:
cache[0] = "It's Good to be Alive!"
if __name__ == "__main__":
for cnt in range(2):dittle()
print(" Attempting to pass an list to dittle()")
dittle([" BAD","Data"])
print(" Attempting to pass a non-list to dittle()")
dittle("hi")
print(" Calling dittle() normally..")
dittle()
print(" Attempting to set the private mutable value from the outside.")
dittle([" I am a Grieffer!\n (Notice this change will not stick!)",-7])
print(" Calling dittle() normally once again.")
dittle()
dittle()
Si ejecuta este código, verá que la función dittle () se internaliza en la primera llamada pero no en llamadas adicionales, usa una caché estática privada (la predeterminada mutable) para el almacenamiento estático interno entre llamadas, rechaza los intentos de secuestro el almacenamiento estático, es resistente a la entrada maliciosa y puede actuar en función de las condiciones dinámicas (en este caso, en la cantidad de veces que se ha llamado a la función).
La clave para usar valores predeterminados mutables no es hacer nada que reasigne la variable en la memoria, sino cambiar siempre la variable en su lugar.
Para ver realmente el potencial y la utilidad de esta técnica, guarde este primer programa en su directorio actual con el nombre "DITTLE.py", luego ejecute el siguiente programa. Importa y usa nuestro nuevo comando dittle () sin requerir ningún paso para recordar o programar aros para saltar.
Aquí está nuestro segundo ejemplo. Compile y ejecute esto como un programa nuevo.
from DITTLE import dittle
print("\n We have emulated a new python command with 'dittle()'.\n")
dittle()
dittle()
dittle()
dittle()
dittle()
¿No es eso lo más resbaladizo y limpio posible? Estos valores predeterminados mutables pueden ser realmente útiles.
========================
Después de reflexionar sobre mi respuesta por un tiempo, no estoy seguro de haber marcado la diferencia entre usar el método predeterminado mutable y la forma habitual de lograr lo mismo con claridad.
La forma habitual es utilizar una función importable que envuelva un objeto Class (y utilice un global). Entonces, para comparar, aquí hay un método basado en clases que intenta hacer las mismas cosas que el método predeterminado mutable.
from time import sleep
class dittle_class():
def __init__(self):
self.b = 0
self.a = " Hello World!"
print("\n Initializing Class Object. Executes on First Call only.")
print(" self.a = '"+str(self.a),"', self.b =",self.b,end="\n\n")
def report(self):
self.b = self.b + 1
if self.b == 1:
print(" Dittle() called",self.b,"time.")
else:
print(" Dittle() called",self.b,"times.")
if self.b == 5:
self.a = " It's Great to be alive!"
print(" Internal String =",self.a,end="\n\n")
if self.b ==3:
print(" Let's rest for a moment.")
sleep(2.0)
print(" Wheew! Ready to continue.\n")
sleep(1.0)
cl= dittle_class()
def dittle():
global cl
if type(cl.a) != str and type(cl.b) != int:
print(" Class exists but does not have valid format.")
cl.report()
if __name__ == "__main__":
print(" We have emulated a python command with our own 'dittle()' command.\n")
for cnt in range(2):dittle()
print(" Attempting to pass arguments to dittle()")
try:
dittle(["BAD","Data"])
except:
print(" This caused a fatal error that can't be caught in the function.\n")
print(" Calling dittle() normally..")
dittle()
print(" Attempting to set the Class variable from the outside.")
cl.a = " I'm a griefer. My damage sticks."
cl.b = -7
dittle()
dittle()
Guarde este programa basado en clases en su directorio actual como DITTLE.py y luego ejecute el siguiente código (que es el mismo que antes).
from DITTLE import dittle
dittle()
dittle()
dittle()
dittle()
dittle()
Al comparar los dos métodos, las ventajas de usar un valor predeterminado mutable en una función deberían ser más claras. El método predeterminado mutable no necesita globales, sus variables internas no se pueden configurar directamente. Y aunque el método mutable aceptó un argumento aprobado con conocimiento para un solo ciclo y luego lo ignoró, el método Class se alteró permanentemente porque su variable interna está directamente expuesta al exterior. ¿En cuanto a qué método es más fácil de programar? Creo que eso depende de su nivel de comodidad con los métodos y la complejidad de sus objetivos.