Pour répondre à la question des bonnes utilisations des valeurs mutables des arguments par défaut, je propose l'exemple suivant :
Une valeur par défaut mutable peut être utile pour programmer des commandes faciles à utiliser et importables de votre propre création. La méthode du défaut mutable revient à avoir des variables statiques privées dans une fonction que vous pouvez initialiser lors du premier appel (un peu comme une classe) mais sans avoir à recourir aux globales, sans avoir à utiliser un wrapper, et sans avoir à instancier un objet de classe qui a été importé. C'est à sa manière élégant, et j'espère que vous en conviendrez.
Prenons ces deux exemples :
def dittle(cache = []):
from time import sleep # Not needed except as an example.
# dittle's internal cache list has this format: cache[string, counter]
# Any argument passed to dittle() that violates this format is invalid.
# (The string is pure storage, but the counter is used by dittle.)
# -- Error Trap --
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
# -- Initialize Function. (Executes on first call only.) --
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")
# -- Normal Operation --
cache[1]+=1 # Static cycle count.
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) # Since we imported it, we might as well use it.
print(" Wheew! Ready to continue.\n")
sleep(1.0)
elif cache[1] == 4:
cache[0] = "It's Good to be Alive!" # Let's change the private message.
# =================== MAIN ======================
if __name__ == "__main__":
for cnt in range(2):dittle() # Calls can be loop-driven, but they need not be.
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.")
# Even an insider's attempt to feed a valid format will be accepted
# for the one call only, and is then is discarded when it goes out
# of scope. It fails to interrupt normal operation.
dittle([" I am a Grieffer!\n (Notice this change will not stick!)",-7])
print(" Calling dittle() normally once again.")
dittle()
dittle()
Si vous exécutez ce code, vous verrez que la fonction dittle() internalise lors du tout premier appel mais pas lors des appels suivants, qu'elle utilise un cache statique privé (le défaut mutable) pour le stockage statique interne entre les appels, qu'elle rejette les tentatives de détournement du stockage statique, qu'elle est résistante aux entrées malveillantes et qu'elle peut agir sur la base de conditions dynamiques (ici sur le nombre de fois que la fonction a été appelée).
La clé de l'utilisation des valeurs par défaut mutables n'est pas de faire quoi que ce soit qui puisse réaffecter la variable en mémoire, mais de toujours changer la variable en place.
Pour vous rendre compte de la puissance et de l'utilité potentielles de cette technique, enregistrez ce premier programme dans votre répertoire courant sous le nom "DITTLE.py", puis exécutez le programme suivant. Il importe et utilise notre nouvelle commande dittle() sans qu'il soit nécessaire de se souvenir d'une quelconque étape ou d'un quelconque obstacle à la programmation.
Voici notre deuxième exemple. Compilez et exécutez ce programme en tant que nouveau programme.
from DITTLE import dittle
print("\n We have emulated a new python command with 'dittle()'.\n")
# Nothing to declare, nothing to instantize, nothing to remember.
dittle()
dittle()
dittle()
dittle()
dittle()
N'est-ce pas là une façon de faire aussi simple et propre que possible ? Ces valeurs par défaut modifiables peuvent s'avérer très utiles.
\========================
Après avoir réfléchi à ma réponse, je ne suis pas sûr d'avoir fait la différence entre l'utilisation de la méthode mutable par défaut et celle de la méthode normale par défaut. d'accomplir la même chose.
La méthode habituelle consiste à utiliser une fonction importable qui englobe un objet Class (et utilise un global). À titre de comparaison, voici une méthode basée sur une classe qui tente de faire les mêmes choses que la méthode mutable par défaut.
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) # Since we imported it, we might as well use it.
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()
# =================== MAIN ======================
if __name__ == "__main__":
print(" We have emulated a python command with our own 'dittle()' command.\n")
for cnt in range(2):dittle() # Call can be loop-driver, but they need not be.
print(" Attempting to pass arguments to dittle()")
try: # The user must catch the fatal error. The mutable default user did not.
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()
Enregistrez ce programme basé sur les classes dans votre répertoire courant sous le nom de DITTLE.py puis exécutez le code suivant (qui est le même que précédemment).
from DITTLE import dittle
# Nothing to declare, nothing to instantize, nothing to remember.
dittle()
dittle()
dittle()
dittle()
dittle()
En comparant les deux méthodes, les avantages de l'utilisation d'un défaut mutable dans une fonction devraient être plus clairs. La méthode mutable par défaut n'a pas besoin de globaux, ses variables internes ne peuvent pas être définies directement. Et alors que la méthode mutable a accepté un argument transmis pour un cycle unique puis l'a ignoré, la méthode Class a été altérée de façon permanente parce que ses variables internes sont directement exposées à l'extérieur. Quant à savoir quelle méthode est la plus facile à programmer ? Je pense que cela dépend de votre niveau de confort avec les méthodes et de la complexité de vos objectifs.