# This is bad way of definition default argument. Lets see what will happens
# if we will use this in real code.
def bad_func(element, aggregator=[]):
aggregator.append(element)
return aggregator
# Here's we're invoking our bad_func a few times.
br1 = bad_func(1)
print "br1:", br1
br2 = bad_func(2)
print "br2:", br2
non_default_aggregator = ["a", "b", "c"]
br3 = bad_func(4, non_default_aggregator)
print "br3:", br3
br4 = bad_func("new element")
print "br4:", br4
# As you can see the result behave in different way we've expect, so what is
# actually happens? We shall investigate this!
print "br1: {0}, br2: {1}, br4: {2}".format(id(br1), id(br2), id(br4))
print "br1 == br2 == br4:", id(br1) == id(br2) == id(br4)
print "br3: {0}, nda: {1}".format(id(br3), id(non_default_aggregator))
print "br3 == nda:", id(br3) == id(non_default_aggregator)
# So, as we can see three of these outputs referres to the same object. How to
# fix this situation and enforce function to behave correctly?
print
# This is the way it goes. And it's a good practice.
def good_func(element, aggregator=None):
if aggregator is None:
aggregator = []
aggregator.append(element)
return aggregator
# Here's we're invoking our good_func a few times.
br1 = good_func(1)
print "br1:", br1
br2 = good_func(2)
print "br2:", br2
non_default_aggregator = ["a", "b", "c"]
br3 = good_func(4, non_default_aggregator)
print "br3:", br3
br4 = good_func("new element")
print "br4:", br4
# Nice. Now we have what we wanted.
print "br1: {0}, br2: {1}, br4: {2}".format(id(br1), id(br2), id(br4))
print "br1 == br2 == br4:", id(br1) == id(br2) == id(br4)
print "br3: {0}, nda: {1}".format(id(br3), id(non_default_aggregator))
print "br3 == nda:", id(br3) == id(non_default_aggregator)
# So as conclusion. We see that if object were created during function
# definition than default value will keep referring on it on each invokation.