示例:
-测试命名比较随意-
# foo?对象
def add(foo=[]):
"""传入的参数为变量foo,指向的[]在定义函数之前创建,而不是每次执行前重新创建一个新列表对象"""
print(id(foo))
foo.append('end')
print(foo) # 变量foo依然指向添加了元素之后的列表对象
def add_2(foo=5):
"""传入的参数为变量foo,指向的5(int)在定义函数之前创建,同样不是每次执行前重新创建一个int对象"""
print(foo, id(foo))
foo += 1
print(foo, id(foo)) # 创一个int对象:6,变量foo重新指向6
# 如果在函数外先定义好传入的参数呢?
_test = [] # 创建一个新的空列表对象
_test2 = 5 # 定义的变量依然指向之前创建的5
# foo?_test2?对象
def add_3(foo=_test):
print(id(foo))
foo.append('end')
print(foo)
def add_4(foo=_test2):
"""5和6依然为第一次创建的对象"""
print(foo, id(foo))
foo += 1
print(foo, id(foo))
add() # 4386785416 ['end']
add() # 4386785416 ['end', 'end'] 没有重新创建一个空列表对象
add_2() # 5 4384690336 6 4384690368
add_2() # 5 4384690336 6 4384690368
# 函数外定义好参数,其实和上面的没有区别,只是中间多了一个变量的引用而已
add_3() # 4386785352 ['end']
add_3() # 4386785352 ['end', 'end']
add_4() # 5 4384690336 6 4384690368
add_4() # 5 4384690336 6 4384690368
函数add()第二次执行的时候,没有重新创建一个新的空列表对象,而是传入了第一次执行时创建的那个列表对象,原因是函数add()的参数是变量foo,foo储存的是第一次创建的列表的实体地址(python是根据变量中储存的实体地址去查找对象的:寻址),而列表是可变对象,当列表中添加了新的元素之后,对象的实体地址并没有发生变化,变量foo中储存的地址也不会发生变化,所以第二次执行的时候传入的foo变量依然还是第一次的那个列表对象。如下图:
总结:
- 在一个脚本运行的环境中,不可变对象(int等)应该只会创建一次,定义不同的变量的话,不同变量只是储存对象实体的地址,只是增加(_test2增加了一个对象5的引用)或者改变(函数add()中的变量foo,开始指向对象5,+1后指向对象6)变量的引用而已,不会重新再创建一个对象(测试中的int对象5和6,至始至终对象实体没有发生变化)
- 重新执行脚本后所有对象会重新创建
- 函数的默认参数应该为不可变对象,测试中的函数add()和add_3()传递列表参数不可取,正确姿势如下:
def add_end(foo=None):
if foo is None:
# 执行时会重新创建一个新的空列表对象
foo = []
foo.append('END')
return foo