其他人说的是真的……默认是在创建函数时评估的,但并不是在创建时就取“i的值”。在创建时默认分配由“i”引用的对象。这一点很重要,因为如果该对象是可变的,则默认值可以更改!
发生了什么:
import inspect
i = 5 # name "i" refers to an immutable Python integer object of value 5.
print(f'i = {i} (id={id(i)})') # Note value and ID
# Create function "f" with a parameter whose default is the object
# referred to by name "i" *at this point*.
def f(arg=i):
print(f'arg = {arg} (id={id(arg)})')
# Use the inspect module to extract the defaults from the function.
# Note the value and ID
defaults = dict(inspect.getmembers(f))['__defaults__']
print(f'defaults = {defaults} (id={id(defaults[0])})')
# name "i" now refers to a different immutable Python integer object of value 6.
i = 6
print(f'i = {i} (id={id(i)})') # Note value and ID (changed!)
f() # default for function still referes to object 5.
f(i) # override the default with object currently referred to by name "i"
输出:
i = 5 (id=2731452426672) # Original object
defaults = (5,) (id=2731452426672) # default refers to same object
i = 6 (id=2731452426704) # Different object
arg = 5 (id=2731452426672) # f() default is the original object
arg = 6 (id=2731452426704) # f(i) parameter is different object
现在查看可变默认值的结果:
import inspect
i = [5] # name "i" refers to an mutable Python list containing immutable integer object 5
print(f'i = {i} (id={id(i)})') # Note value and ID
# Create function "f" with a parameter whose default is the object
# referred to by name "i" *at this point*.
def f(arg=i):
print(f'arg = {arg} (id={id(arg)})')
# Use the inspect module to extract the defaults from the function.
# Note the value and ID
defaults = dict(inspect.getmembers(f))['__defaults__']
print(f'defaults = {defaults} (id={id(defaults[0])})')
# name "i" now refers to a different immutable Python integer object of value 6.
i[0] = 6 # MUTATE the content of the object "i" refers to.
print(f'i = {i} (id={id(i)})') # Note value and ID (UNCHANGED!)
f() # default for function still refers to original list object, but content changed!
i = [7] # Create a different list object
print(f'i = {i} (id={id(i)})') # Note value and ID (changed)
f(i) # override the default currently refered to by name "i"
输出:
i = [5] (id=2206901216704) # Original object
defaults = ([5],) (id=2206901216704) # default refers to original object
i = [6] (id=2206901216704) # Still original object, but content changed!
arg = [6] (id=2206901216704) # f() default refers to orginal object, but content changed!
i = [7] (id=2206901199296) # Create a new list object
arg = [7] (id=2206901199296) # f(i) parameter refers to new passed object.
如果没有很好地理解,这可能会产生奇怪的副作用:
>>> def f(a,b=[]): # mutable default
... b.append(a)
... return b
...
>>> x = f(1)
>>> x
[1]
>>> y = f(2) # some would think this would return [2]
>>> y
[1, 2]
>>> x # x changed from [1] to [1,2] as well!
[1, 2]
上面,b 指的是原始的默认列表对象。附加到它会改变默认列表。返回它会使x 引用同一个对象。默认列表现在包含[1],因此在第二次调用中附加为[1,2]。 y 与 x 引用相同的默认对象,因此两个名称引用的对象内容相同。
要解决此问题,请使默认值不可变并在看到默认值时创建一个新列表:
>>> def f(a,b=None):
... if b is None:
... b = []
... b.append(a)
... return b
...
>>> f(1)
[1]
>>> f(2)
[2]