深入讲解Python函数中参数的使用及默认参数的陷阱
C++里函数可以设置缺省参数,Java不可以,只能通过重载的方式来实现,python里也可以设置默认参数,最大的好处就是降低函数难度,函数的定义只有一个,并且python是动态语言,在同一名称空间里不能有想多名称的函数,如果出现了,那么后出现的会覆盖前面的函数。 def power(x,n=2): s = 1 while n > 0: n = n - 1 s = s * x return s 看看结果: >>> power(5) 25 >>> power(5,3) 125 注意: 必选参数在前,默认参数在后,否则Python的解释器会报错。 def add_end(L=[]): L.append('END') return L 看看调用结果: >>> add_end([1,2,3]) [1,3,'END'] >>> add_end(['x','y','z']) ['x','z','END'] >>> add_end() ['END'] >>> add_end() ['END','END'] >>> add_end() ['END','END','END'] 这里需要解释一下,Python函数在定义的时候,默认参数L的值就被计算出来了,即[]。此时L指向[]。所以如果L中的内容改变了,下次调用引用的内容也就不再是[]了。所以要牢记一点定义默认参数必须指向不可变对象!。 可变参数 def calc(numbers): sum = 0 for n in numbers: sum = sum + n * n return sum 调用方式: >>> calc([1,3]) 14 >>> calc((1,5,7)) 84 第二种方式,直接传入多个参数,函数内部会自动用一个tuple接收。 def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum 调用方式: >>> calc(1,2) 5 >>> calc() 0 这个时候如果还想把一个list或者tuple里的数据传进去,可以这样: >>> nums = [1,3] >>> calc(*nums) 14 关键字参数 def person(name,age,**kw): print 'name:',name,'age:','other:',kw 调用示例: >>> person('Michael',30) name: Michael age: 30 other: {} >>> person('Bob',35,city='Beijing') name: Bob age: 35 other: {'city': 'Beijing'} >>> person('Adam',45,gender='M',job='Engineer') name: Adam age: 45 other: {'gender': 'M','job': 'Engineer'} 参数组合 def fact(n): if n==1: return 1 return n * fact(n - 1) 如果传入的n很大,就可能会溢出,这是由于return n * fact(n - 1)引入了乘法表达式,就不是尾递归了。把代码改一下: def fact(n): return fact_iter(n,1) def fact_iter(num,product): if num == 1: return product return fact_iter(num - 1,num * product) 默认参数陷阱 def add_end(L=[]): L.append('END') return L 调用函数的结果: >>> add_end([1,'END'] 很明显这个与函数的定义初衷不符,用一句话解释就是: # coding=utf-8 def a(): print "a executed" return [] def b(x=a()): print "id(x):",id(x) x.append(5) print "x:",x for i in range(2): print "不带参数调用,使用默认参数" b() print b.__defaults__ print "id(b.__defaults__[0]):",id(b.__defaults__[0]) for i in range(2): print "带参数调用,传入一个list" b(list()) print b.__defaults__ print "id(b.__defaults__[0]):",id(b.__defaults__[0]) NOTE:稍微解释一下,所有默认值都存储在函数对象的__defaults__属性中,这是一个列表,每一个元素均为一个默认参数值。 a executed 不带参数调用,使用默认参数 id(x): 140038854650552 x: [5] ([5],) id(b.__defaults__[0]): 140038854650552 不带参数调用,使用默认参数 id(x): 140038854650552 x: [5,5] ([5,5],) id(b.__defaults__[0]): 140038854650552 带参数调用,传入一个list id(x): 140038854732400 x: [5] ([5,) id(b.__defaults__[0]): 140038854650552 带参数调用,传入一个list id(x): 140038854732472 x: [5] ([5,) id(b.__defaults__[0]): 140038854650552 简单分析一下输出结果: print b.__defaults__ 输出结果为 复制代码 代码如下: ([5],) 第7~11行 第二次循环,代码第14行调用b()没有传递参数,使用默认参数。 注意:默认参数只会计算一次,也就是说那个内存区域就固定了,但是这个地址所指向的是一个list,内容可以改变,此时由于上一次调用x: [5],所以 print b.__defaults__ 输出结果为 ([5,) 第12~16行 print b.__defaults__ 输出结果为
复制代码 代码如下: ([5],) 第18~21行 第二个循环语句,第二次循环,代码第20行传入一个空的list,所以也不使用默认参数,此时仍然是x=[],所以 print b.__defaults__ 输出结果依然为
复制代码 代码如下: ([5],) 函数也是对象,因此定义的时候就被执行,默认参数是函数的属性,它的值可能会随着函数被调用而改变。其他对象不都是如此吗? 牢记: 默认参数必须指向不变对象!代码改一下如下: # coding=utf-8 def a(): print "a executed" return None def b(x=a()): print "id(x):",id(x) if x is None: x = [] x.append(5) print "x:",id(b.__defaults__[0]) 此时的输出结果看看是什么: a executed 不带参数调用,使用默认参数 id(x): 9568656 x: [5] (None,) id(b.__defaults__[0]): 9568656 不带参数调用,使用默认参数 id(x): 9568656 x: [5] (None,) id(b.__defaults__[0]): 9568656 带参数调用,传入一个list id(x): 140725126699632 x: [5] (None,) id(b.__defaults__[0]): 9568656 带参数调用,传入一个list id(x): 140725126699704 x: [5] (None,) id(b.__defaults__[0]): 9568656 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |