(三)参数绑定、对象修改与默认值
1. 修改对象与重新绑定名称
第二章已经见过两个名称指向同一个列表。函数参数也沿用同样的规则:调用时把形参绑定到实参对象,不自动复制对象。
# append 修改共享的列表;随后赋值只改变函数内 values 的绑定。
def adjust(values):
values.append(45)
values = [0]
return values
minutes = [20, 35]
replacement = adjust(minutes)
print(minutes)
print(replacement)输出 [20, 35, 45] 和 [0]。进入函数时,minutes 与 values 指向同一列表;append 修改它,两处都能读到新内容。执行 values = [0] 后,局部名称指向新列表,外面的 minutes 仍指向原来的列表。
因此,“函数能修改列表”与“函数能替调用者重新绑定变量”是两件事。需要交回新列表时,用 return,再由调用者写 minutes = adjust(minutes)。需要独立修改时,也可以传入第二章学过的副本;嵌套数据仍要分清浅拷贝与深拷贝。
2. 可变默认参数的连续调用
希望“不传清单时创建一份清单”,容易写成下面这样。先观察它实际保存了什么:
# 观察示例:[] 在执行 def 时建立一次,后续省略 items 的调用共用它。
def add_title(title, items=[]):
items.append(title)
return items
first = add_title("Python")
second = add_title("NumPy")
print(first)
print(second)
print(first is second)两个列表输出都是 ['Python', 'NumPy'],最后是 True。第一次返回后,first 已经指向那份默认列表;第二次向同一列表追加,first 读到的内容也随之改变。
默认表达式在执行定义时求值,不会在每次调用时重新求值。把“缺省时新建”的动作放进函数体,就会在调用期间执行:
# None 表示这次没有交入清单;进入函数后为这次调用建立新列表。
def add_title(title, items=None):
if items is None:
items = []
items.append(title)
return items
first = add_title("Python")
second = add_title("NumPy")
print(first, second, first is second)
# 主动传入一个列表时,仍然在该列表中追加。
shared = []
add_title("Pygame", shared)
print(shared)输出 ['Python'] ['NumPy'] False,以及 ['Pygame']。这里使用 is None 检查是否收到那个特殊值;若写成 if not items,调用者主动传入的空列表也会被换成新列表。
整数默认值同样在定义时选定。例如先设 goal = 30,再定义 def check(minutes, target=goal),之后把外面的 goal 改成 60,省略 target 的调用仍使用定义时保存的 30。默认参数求值