In answer to the question of good uses for mutable default argument values, I offer the following example:
A mutable default can be useful for programing easy to use, importable commands of your own creation. The mutable default method amount to having private, static variables in a function that you can initialization on the first call (very much like a class) but without having to resort to globals, without having to use a wrapper, and without having to instantize a class object that was imported. It is in its own way elegant, as I hope you will agree.
Consider these two examples:
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()
If you run this code, you will see that the dittle() function internalizes on the the very first call but not on additional calls, it uses a private static cache (the mutable default) for internal static storage between calls, rejects attempts to hijack the static storage, is resilient to malicious input, and can act based on dynamic conditions (here on the number of times the function has been called.)
The key to using mutable defaults not to do anything what will reassign the variable in memory, but to always change the variable in place.
To really see the potential power and usefulness of this technique, save this first program to your current directory under the name "DITTLE.py", then run the next program. It imports and uses our new dittle() command without requiring any steps to remember or programing hoops to jump through.
Here is our second example. Compile and run this as a new program.
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()
Now isn't that as slick and clean as can be? These mutable defaults can really come in handy.
========================
After reflecting on my answer for a while, I'm not sure that I made the difference between using the mutable default method and the regular
way of accomplishing the same thing clear.
The regular way is to use an importable function that wraps a Class object (and uses a global). So for comparison, here a Class-based method that attempts to do the same things as the mutable default method.
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()
Save this Class-based program in your current directory as DITTLE.py
then run the following code (which is the same as earlier.)
from DITTLE import dittle
# Nothing to declare, nothing to instantize, nothing to remember.
dittle()
dittle()
dittle()
dittle()
dittle()
By comparing the two methods, the advantages of using a mutable default in a function should be clearer. The mutable default method needs no globals, it's internal variables can't be set directly. And while the mutable method accepted a knowledgeable passed argument for a single cycle then shrugged it off, the Class method was permanently altered because its internal variable are directly exposed to the outside. As for which method is easier to program? I think that depends on your comfort level with the methods and the complexity of your goals.
__init__
function for a class, which gets set into an instance variable; this is a perfectly valid thing to want to do, and it all goes horribly wrong with a mutable default. stackoverflow.com/questions/43768055/… - Mark Ransom