Part 1: Immutable object
If two variables refer to immutable objects that have equal values (a == b is True), in practice it rarely matters if they refer to copies or are aliases referring to the same object because the value of an immutable object does not change, with one exception. The exception is immutable collections such as tuples and frozensets: if an immutable collection holds references to mutable items, then its value may actually change when the value of a mutable item changes. In practice, this scenario is not so common. What never changes in an immutable collection are the identities of the objects within.
Part 2: Function parameters
(1) Function parameters are passed as aliases, which means the function may change any mutable object received as an argument. There is no way to prevent this, except making local copies or using immutable objects (e.g., passing a tuple instead of a list).
class Bus: def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = passengers#list(passengers)
def pick(self, name):
self.passengers.append(name) def drop(self, name):
self.passengers.remove(name)abcd = ['Alice', 'Bill', 'Claire', 'David']
bus1 = Bus(abcd)
bus1.drop('Bill')
print(bus1.passengers)
print(abcd)
Output
['Alice', 'Claire', 'David']
['Alice', 'Claire', 'David']
In the example, if we change self.passengers=list(passengers)
then abcd
list will not change.
(2) Using mutable objects as default values for function parameters is dangerous because if the parameters are changed in place, then the default is changed, affecting every future call that relies on the default.
class HauntedBus:
"""A bus model haunted by ghost passengers"""
def __init__(self, passengers=[]): # <1>
self.passengers = passengers # <2>
def pick(self, name):
self.passengers.append(name) # <3>
def drop(self, name):
self.passengers.remove(name)
see haunted_bus.py
Part 3: deepcopy
>>> import copy
>>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
>>> bus2 = copy.copy(bus1)
>>> bus3 = copy.deepcopy(bus1)
>>> bus1.drop('Bill')
>>> bus2.passengers['Alice', 'Claire', 'David']
>>> bus3.passengers['Alice', 'Bill', 'Claire', 'David']