python 经典的参数错误

Python
In [29]: def f(x,l=[]): ...: for i in range(x): ...: l.append(i) ...: print(l) ...: In [30]: f(2) [0, 1] In [31]: f(3,[3,2,1]) [3, 2, 1, 0, 1, 2] In [32]: f(3) [0, 1, 0, 1, 2] In [33]: dir(f) Out[33]: ['__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__'] In [34]: f.__defaults__ Out[34]: ([0, 1, 0, 1, 2],) In [35]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
In [ 29 ] : def f ( x , l = [ ] ) :
. . . : for i in range ( x ) :
. . . : l . append ( i )
. . . : print ( l )
. . . :
 
In [ 30 ] : f ( 2 )
[ 0 , 1 ]
 
In [ 31 ] : f ( 3 , [ 3 , 2 , 1 ] )
[ 3 , 2 , 1 , 0 , 1 , 2 ]
 
In [ 32 ] : f ( 3 )
[ 0 , 1 , 0 , 1 , 2 ]
 
In [ 33 ] : dir ( f )
Out [ 33 ] :
[ '__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__' ]
 
In [ 34 ] : f . __defaults__
Out [ 34 ] : ( [ 0 , 1 , 0 , 1 , 2 ] , )
 
In [ 35 ] :
Python
def f(x,l=[]): for z in range(x): l.append(z) print(l) if __name__ == '__main__': print(f.__defaults__) f(3) print(f.__defaults__) f(3,["a",'b']) print(f.__defaults__) f(3) print(f.__defaults__)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def f ( x , l = [ ] ) :
     for z in range ( x ) :
         l . append ( z )
 
     print ( l )
 
 
if __name__ == '__main__' :
     print ( f . __defaults__ )
     f ( 3 )
     print ( f . __defaults__ )
     f ( 3 , [ "a" , 'b' ] )
     print ( f . __defaults__ )
     f ( 3 )
     print ( f . __defaults__ )

 

Python
有网友问 >>> def foo(bar=[]): ... bar.append("baz") ... return bar >>> foo() ["baz"] >>> foo() ["baz", "baz"] >>> foo() ["baz", "baz", "baz"] 为什么多次调用foo()bar的值不会重新赋值为[]呢?bar的作用域不是应该在foo函数之内吗? 文档中是这么说的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
有网友问
 
>>> def foo ( bar = [ ] ) :
. . . bar . append ( "baz" )
. . . return bar
>>> foo ( )
[ "baz" ]
>>> foo ( )
[ "baz" , "baz" ]
 
>>> foo ( )
 
[ "baz" , "baz" , "baz" ]
 
为什么多次调用 foo ( ) bar的值不会重新赋值为 [ ]呢? bar的作用域不是应该在 foo函数之内吗?
 
文档中是这么说的。

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:

重要警告:默认值只计算一次。当默认值是可变对象(如列表,字典或大多数类的实例)时,这会有所不同。例如,以下函数会累积在后续调用中传递给它的参数

 

例子一:

Python
i = 5 def f(arg=i): print(arg) i = 6 f()
1
2
3
4
5
6
7
i = 5
 
def f ( arg = i ) :
     print ( arg )
 
i = 6
f ( )

 

 

输出值为5,不是6

例子二:

Python
def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3))
1
2
3
4
5
6
7
def f ( a , L = [ ] ) :
     L . append ( a )
     return L
 
print ( f ( 1 ) )
print ( f ( 2 ) )
print ( f ( 3 ) )

输出值为

[1]

[1, 2]

[1, 2, 3]

这个原因是由于默认参数只计算一次,因为list 是可变数据类型,函数每次调用时,L 是同一个对象的引用。

加一个id(),可以判断每次函数的调用都是访问的同一个list 实例对象。

Python
def f(a, L=[]): L.append(a) print(id(L)) return L print(f(1)) print(f(2)) print(f(3))
1
2
3
4
5
6
7
8
9
def f ( a , L = [ ] ) :
     L . append ( a )
     print ( id ( L ) )
     return L
 
 
print ( f ( 1 ) )
print ( f ( 2 ) )
print ( f ( 3 ) )

Python
先定义一个函数,传入一个list,添加一个END再返回: def add_end(L=[]): L.append('END') return L 当你正常调用时,结果似乎不错: >>> add_end([1, 2, 3]) [1, 2, 3, 'END'] >>> add_end(['x', 'y', 'z']) ['x', 'y', 'z', 'END'] 当你使用默认参数调用时,一开始结果也是对的: >>> add_end() ['END'] 但是,再次调用add_end()时,结果就不对了: >>> add_end() ['END', 'END'] >>> add_end() ['END', 'END', 'END'] 很多初学者很疑惑,默认参数是[],但是函数似乎每次都“记住了”上次添加了'END'后的list。 原因解释如下: Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。 所以,定义默认参数要牢记一点:默认参数必须指向不变对象! 要修改上面的例子,我们可以用None这个不变对象来实现: def add_end(L=None): if L is None: L = [] L.append('END') return L 现在,无论调用多少次,都不会有问题: >>> add_end() ['END'] >>> add_end() ['END'] 为什么要设计str、None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
先定义一个函数,传入一个 list,添加一个 END再返回:
 
def add_end ( L = [ ] ) :
     L . append ( 'END' )
     return L
当你正常调用时,结果似乎不错:
 
>>> add_end ( [ 1 , 2 , 3 ] )
[ 1 , 2 , 3 , 'END' ]
>>> add_end ( [ 'x' , 'y' , 'z' ] )
[ 'x' , 'y' , 'z' , 'END' ]
当你使用默认参数调用时,一开始结果也是对的:
 
>>> add_end ( )
[ 'END' ]
但是,再次调用 add_end ( )时,结果就不对了:
 
>>> add_end ( )
[ 'END' , 'END' ]
>>> add_end ( )
[ 'END' , 'END' , 'END' ]
很多初学者很疑惑,默认参数是 [ ],但是函数似乎每次都“记住了”上次添加了 'END'后的 list。
 
原因解释如下:
 
Python函数在定义的时候,默认参数 L的值就被计算出来了,即 [ ],因为默认参数 L也是一个变量,它指向对象 [ ],每次调用该函数,如果改变了 L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的 [ ]了。
 
所以,定义默认参数要牢记一点:默认参数必须指向不变对象!
 
要修改上面的例子,我们可以用 None这个不变对象来实现:
 
def add_end ( L = None ) :
     if L is None :
         L = [ ]
     L . append ( 'END' )
     return L
现在,无论调用多少次,都不会有问题:
 
>>> add_end ( )
[ 'END' ]
>>> add_end ( )
[ 'END' ]
为什么要设计 str、 None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。




  • zeropython 微信公众号 5868037 QQ号 5868037@qq.com QQ邮箱
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值