Object References, Mutability, and Recycling

1. Function Parameters as References

The only mode of parameter passing in Python is call by sharing(共享传参). Call by sharing means that each formal parameter of the function gets a copy of each reference in the arguments. In other words, the parameters inside the function become aliases of the actual arguments. 

The result of this scheme is that a function may change any mutable object passed as a parameters, but it cannot change the identity of those objects. 

Example 8-11. A function may change any mutable object it receives.

"""A function may change any mutable object it receives"""


>>> def f(a, b):
...     a += b
...     return a
...
>>> x = 1
>>> y = 2
>>> f(x, y)
3
>>> x, y        # The number x is unchanged.
(1, 2)
>>>
>>> a = [1, 2]
>>> b = [3, 4]
>>> f(a, b)
[1, 2, 3, 4]
>>> a, b        # The list a is changed.
([1, 2, 3, 4], [3, 4])
>>>
>>>
>>> t = (10, 20)
>>> u = (30, 40)
>>> f(t, u)
(10, 20, 30, 40)
>>> t, u        # The tuple t is unchanged.
((10, 20), (30, 40))
>>>

Python 函数传参参考:https://www.cnblogs.com/zhoug2020/p/9110663.html

2. Mutable Types as Parameter Defaults: Bad idea

You should avoid mutable objects as default values for parameters.

EXample 8-12. A simple class to illustrate the danger of a mutable default.

class HauntedBus:

    def __init__(self, passengers=[]):  # When the passengers argument is not given, this parameter is bound to the default list object, which is initially empty.
        self.passengers = passengers    # This assignment makes self.passenger an alias for passengers, which is itself an alias for the default list, when no passengers argument is given.

    def pick(self, name):
        self.passengers.append(name)    # When the methods .remove() and .append() are used with self.passengers, we are actually mutating the default list, which is an attribute of the function object.

    def drop(self, name):
        self.passengers.remove(name)


# Example 8-13 shows the eerie behavior of the HauntedBus
"""
>>> bus1 = HauntedBus(['Alice', 'Bill'])
>>> bus1.passengers
['Alice', 'Bill']
>>> bus1.pick('Charlie')
>>> bus1.drop('Alice')
>>> bus1.passengers     # bus1 正常运行
['Bill', 'Charlie']
>>> 
>>> bus2 = HauntedBus()     # bus2 starts empty, so the default empty list is assigned to self.passengers.
>>> bus2.pick('Carrie')
>>> bus2.passengers
['Carrie']
>>> 
>>> bus3 = HauntedBus()     # bus3 also starts empty, again the default list is assigned.
>>> bus3.passengers         # The default is no longer empty!
['Carrie']
>>> bus3.pick('Dave')
>>> bus2.passengers     # Now Dave, picked by bus3, appears in bus2.
['Carrie', 'Dave']
>>> 
>>> bus2.passengers is bus3.passengers      # bus2.passengers and bus3.passengers refer to the same list.
True
>>> bus1.passengers     # But bus1.passengers is a distinct list.
['Bill', 'Charlie']
"""

# The problem is that Bus instances that don't get an initial passenger list end up sharing the same passenger list among themselves.


"""
Strange things happen only when a HauntedBus starts empty, because then self.passengers becomes am alias for the default
value of the passengers parameter. The problem is that each default value is evaluated when the function is defined --
i.e., usually when the module is loaded -- and the default values become attributes of the function object. So if a
default value is a mutable object, and you change it, the change will affect every future call of the function.
"""

# Inspect the HauntedBus.__init__ object and see the students haunting its __defaults__ attribute:
"""
>>> dir(HauntedBus.__init__)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> HauntedBus.__init__.__defaults__
(['Carrie', 'Dave'],)
>>> 
"""
# Finally, we can verify that bus2.passengers is an alias bound to the first element of the HauntedBus.__init__.__defaults__ attributes:
"""
>>> HauntedBus.__init__.__defaults__[0] is bus2.passengers
True
"""

# The issue with mutable defaults explains why None is often used as the default value for parameters that may receive
# mutable values.

end...

猜你喜欢

转载自www.cnblogs.com/neozheng/p/12275256.html