在Python教程里,针对默认参数,给了一个“重要警告”的例子:javascript
def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3))
默认值只会执行一次,也没说缘由。会打印出结果:java
[1] [1, 2] [1, 2, 3]
由于学的第一门语言是Ruby,因此感受有些奇怪。 但确定的是方法f必定储存了变量L。python
p指向不可变对象,好比数字。则至关于p指针指向了不一样的内存地址。git
p指向的是可变对象,好比list。list自身的改变,并不会改变list对象自身所在的内存地址。因此p指向的内存地址不变。github
>>> p = 1 >>> id(p) 4523801232 >>> p = p + 1 >>> id(p) 4523801264 >>> p = 11 >>> id(p) 4523801552 >>> p = [] >>> id(p) 4527080640 >>> p.append(11) >>> id(p) 4527080640
Python函数的参数默认值,是在编译阶段就绑定了。(写代码时就定义了。)app
下面是一段从Python Common Gotchas中摘录的缘由解释:ide
Python’s default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby). This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.函数
由此可知:ui
第三条,修改上面的例子:lua
def f(a, L = 1): L = a print(id(L)) return L print("self",id(f.__defaults__[0])) print(f(1)) print("self",id(f.__defaults__[0])) print(f(33)) print("self",id(f.__defaults__[0])) #运行结果: self 4353170064 4353170064 1 self 4353170064 4353171088 33 self 4353170064
默认参数L,在编译阶段就绑定了,储存在__default__内。函数体内的L = a表达式,生成的是新的变量。返回的L是新的变量,和默认参数无关。
第四条,仍是上面的例子, 改一下默认参数的类型为可变对象list:
def f(a, L = []): L.append(a) print(id(L)) return L # L = f(1) print("self",id(f.__defaults__[0])) print(f(1)) print("self",id(f.__defaults__[0])) print(f(33)) print("self",id(f.__defaults__[0]))
#返回结果 self 4337586048 4337586048 [1] self 4337586048 4337586048 [1, 33] self 4337586048
由id号可知,返回的是默认参数自身。
def f(a, L = None): if L is None: L = [] L.append(a) return L
StackOverflow 上争论不少。
Ruby之因此没有这个问题,我想是由于Ruby的def关键字定义了一个封闭做用域,任何数据都必须经过参数传入到方法内,才能用。
和Ruby比,Python参数的做用被大大消弱了。Python的出现晚于Ruby,其创始人确定参考了Ruby的设计。抛弃了这个设计,选择了相似javascript的函数方式。def定义的函数,执行时是能够接收外部做用域的变量的。
有观点认为:
出于Python编译器的实现方式考虑,函数是一个内部一级对象。而参数默认值是这个对象的属性。在其余任何语言中,对象属性都是在对象建立时作绑定的。所以,函数参数默认值在编译时绑定也就不足为奇了。
本文参考了:http://cenalulu.github.io/python/default-mutable-arguments/#toc1 ,并加入了本身的理解。欢迎转载!