这里我们看看Python中函数定义的语法,函数的局部变量,函数的参数,Python中函数的形参可以有默认值,参数的传递是赋值操作,在函数调用时,可以对实参进行打包和解包
1,函数定义
关键字def引出函数定义,后面跟着函数名以及用括号括起来的一系列参数,然后从下一行开始函数体(function body),并且要缩进。
生成一个Fibnacci数列的序列,最大不超过某个数的函数
def fib(n):
'''get a list of fibnacci series to n'''
a, b = 0, 1
result = []
while a<n:
result.append(a)
a, b = b, a+b
return result
运行:
>>> fib(3000)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584]
2,函数的局部变量
[这里的符号表(symbol table)等同于命名空间(namespace)]
函数体的执行会为这个函数的局部变量引入一个新的符号表(symbol table)。所有函数体中的赋值语句(assignment statement),都会把变量名和值存在这个符号表中。
而函数体中的引用一个变量时,首先查看函数的符号表,如果这个函数定义包裹在其它函数定义中,就依次查看外围函数的符号表,然后查看全局符号表(也就是函数所属的module的符号表),最后查看Python的内置类型和变量的符号表。
函数的行参也是存在于函数的局部符号表中。
一个例子,函数中引用的是在局部符号表中的参数l,而不是全局符号表中的l。
l = ['a', 'b'] #输出[1,2]
def f(l):
print l
if __name__ == "__main__":
f([1,2])
函数调用的实参传递是通过赋值语句做的,所以传递的是object的引用,对于序列这样的可变类型(mutable),按引用传递的话,如果序列做参数在函数体中改变它的值也会影响它在外围符号表的值,这就像C++中的引用
l = ['a', 'b']
def f(l):
l.append("")
if __name__ == "__main__":
f(l)
print l
输出结果是:
['a', 'b', '22']
3,函数的默认参数
3.1 基础
函数可以有默认参数,有默认值的参数必须在没有默认值的参数后面
def f(a, b=5, c="hello"):
print c, a+b
if __name__ == "__main__":
f(3)
f(3, 4, "hi")
函数参数的默认值如果取自一个变量,那这个默认值会在函数定义的地方被计算得到,默认值只会被计算一次
i = 6
def f(n=i):
print n
i = 7
会输出6
3.2当参数的默认值是list这样的可变对象(mutable object)
函数的默认参数只会被计算一次,不管函数被怎么调用,所以当参数是list这样的可变对象时,但在函数体中其值被改变时,再次调用参数的默认值就是改变后的值
def f(n, l=[]):
l.append(n)
print l
if __name__ == "__main__":
f(1)
f(2)
f(3)
猜猜看会输出什么?输出
[1]
[1, 2]
[1, 2, 3]
也就是从f(2)调用开始,l的默认值就变了,不会再重新计算一次默认值了。
怎么避免这样的情况呢?用下面的代码
def f(n, l=None):
if l is None:
l = []
l.append(n)
print l
if __name__ == "__main__":
f(1)
f(2)
f(3)
输出结果:
[1]
[2]
[3]
None是个内置常量,当然不能被改变,每次f被调用就会用这个值给l赋值
[问题?]这里其实我有个待弄明白的问题,默认值是只会被计算一次还是参数只会被初始化一次,也就是符号表是每次函数调用都建立和销毁,还是函数生存期一直存在,所用调用共用一个。
[答案]现在有答案了,函数的符号表,也就是其局部命名空间会在每次调用和返回时进行创建初始化和删除。
4,关键字实参(keyword argument)
实参(argument)是指函数调用时传递进去的参数值(value),区别于形参(parameter)。
Python实参(argument)分为两类,关键字实参和位置实参(positional argument)。
关键字实参就是在调用函数的时候,以name=value的形式传递参数,name就是参数定义的名字,也就是形参;关键字参数指明了参数名,所以可以不用管其调用时候顺序。
位置实参就是只传递value的形式,顾名思义,这要靠实参的位置来匹配形参,关键字参数必须位于位置参数之后 。
举一个简单的例子
def f(a, b, c):
print "a =", a, "b =", b, "c =", c
if __name__ == "__main__":
f(5, c=8, b=2)
输出结果:
a = 5 b = 2 c = 8
5,参数的解包(unpacking)
如果我们有一个list或一个dict,可把它直接作为参数传给一个函数,里面的值可以解包出来传给一个个参数。
5.1 解包为位置实参
可以把一个list或tuple解包,对应的值作为位置参数传递,调用的时候要以*args的形式
>>> range(2,5)
[2, 3, 4]
>>> args=[2,5]
>>> range(*args) #注意调用语法“*args"
[2, 3, 4]
range接受两个参数,给它传入一个tuple[2,5],解包。
5.2 解包为关键字实参
要解包为关键字参数,使用字典。字典的key为形参的name,是字符串,字典value为传递的实参。
语法上在调用的时候以**args的形式
使用4小节的例子,输出结果相同
def f(a, b, c):
print "a =", a, "b =", b, "c =", c
if __name__ == "__main__":
d = {"a":5, "c":8, "b":2}
f(**d) #注意调用语法“**args"
6,参数的打包,传递任意个参数(packing)
可不可以给函数传递任意个参数呢,可以的,多余的实参可以被打包成一个元组(tuple),传给一个形参。
这个行参在定义时前面加上“*”,即*args
一个小例子,把实参打包成tuple输出
def multiple_argu(*args):
print args
if __name__ == "__main__":
multiple_argu('a', 'b', 'c', 'd')
输出了一个tuple:
('a', 'b', 'c', 'd')
参考:
http://docs.python.org/3/glossary.html#term-argument Python文档,术语
http://docs.python.org/2.7/tutorial/controlflow.html#defining-functions Python文档