定义
格式为——def 函数名(参数列表):其中冒号指的是代码块的开始,函数本身也是一个代码块,所以需要以冒号开始。
通过return定义函数的返回值,如果没有return则默认返回提None。
python中万物皆对象,包括函数,而函数名就是一个指向函数对象的变量而已,因此可以把函数名赋值给另一个变量。如下:
print abs(-32432)
a = abs # 将函数名赋值给另一个变量,此时a就具有求绝对值的功能
print a(-32432)
因为函数名是一个变量,所有也可以把函数名存储到map,list等序列中。
math = (abs,max,'min') # 元组的元素为函数
print math[0](-2342) # 2342
print math[1](2,0,10,8) # 10
print math[2] # min 第三个元素为字符串
空函数
内部不进行任何操作的函数叫空函数。java中空函数就是{}中不写代码的函数。python中空函数的定义如下:
def emtpyFunction():
pass # 这个pass一定不能省略,否则报语法错误
注意:其中的pass关键字一定不能省略,省略就会报错。
作为返回值
由于函数名本身就是一个指向函数对象的变量,所以函数的返回值可以是一个函数。如下:
def f(x, n):
return x % n > 0
def getfunc():
return f
print(getfunc()(10, 5)) # False
可以根据传入getfunc()中的参数决定具体返回的函数。
另外,也可将lambda表达式当作返回值。如:
def getfunc(n):
return lambda x: x % n > 0
print(list(filter(getfunc(3), range(11))))
首先getfunc()它返回一个lambda表达式,式中的n为3,也就是返回的是lambda x:x%3>0,即不是3的倍数。
其次filter(),将函数作用于iterable的每一个元素,返回True则保留该元素,否则不保留。它返回的是一个iterator。
最后是list(),将filter()返回的iterator转换成list,并通过print()进行输出。
从这可以看出,使用lambda表达式作为返回值时,可以指定表达式中的一部分参数。
函数嵌套
python中,可以在函数内部定义另一个函数。内部函数引用了外部外部作用域内的变量,那么内部函数就被称为闭包。多次调用外部函数,得到的内部函数并不是同一个。如:
outer(n): o = 3 def inner(x): return n+x+o # 引用了外层函数的参数n以及变量o return inner # 将内部函数作为返回值返回 f = outer(10) # 再调用一次outer(10)得到的函数并不会与f一样。 print(f) # f是一个function,它表示inner(),并且没有立即执行 print(f(1)) # 首先使用outer()获取一个函数,再调用该函数
简单理解:返回的是一个变量,只不过这个变量指向的是函数而已,而函数是可以执行的但返回的时候只是返回变量,并不会执行函数,直到程序执行这个函数(也就是在代码中调用函数名+(参数))时才执行。
由于函数在返回时并没有执行,并且使用了外界的,所以当外界变量发生变化时,会影响返回函数的最终执行结果。如:
def outer():
L = []
for i in range(1, 4):
def inner():
return i * i
L.append(inner)
return L
f1,f2,f3 = outer()
print("f1 = %d,f2 = %d,f3 = %d" % (f1(), f2(), f3())) # f1 = 9,f2 = 9,f3 = 9
函数返回时并没有立即执行,而当所有的函数都返回结束后,i变成了3。所以f1,f2,f3的结果都是一样的。因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果真要引用循环变量或者后继会发生变量的变量,可以再定义一个函数。如下:
def outer():
L = []
def inner(x):
def g():
return x*x
return g
for i in range(1, 4):
L.append(inner(i))
return L
f1,f2,f3 = outer()
print("f1 = %d,f2 = %d,f3 = %d" % (f1(), f2(), f3())) # f1 = 1,f2 = 4,f3 = 9
循环时,i的值被存储到inner()的参数x中,而g引用的是inner中的参数x,已经与原来的i没有关系了。当再次调用inner时参数变成了新的i,返回的g是一个新的函数,它引用的是当前inner()的参数,与上一个g完全不同。所以输出的结果就是不同的。
返回值
def moreReture():
return 2,5,'return' # 返回多个值,其实是将这些值封装成一个元组返回,只不过表象上是返回多个值
print moreReture() # (2, 5, 'return') 是一个元组
x,y,z = moreReture() # 返回的是一个元组,而元组又可以拆解,所以表面上看一个函数可以返回多个值
print x # 2
print y # 5
print z # return
之所以这么写,是因为方便。
参数
默认值
在定义形参时,可以为形参赋值,该值就是形参的默认值。具有默认值的形参,可以不传入实参,并且必须处于所有参数的后面。如:
def add(a,b=3): # 为形参b赋值默认值3
return a+b
print(add(3)) # 此处可以只传入一个参数,则第二个参数b就取默认值3,所以输出结果为6
1,有默认值的形参可以不传入实参,也可以传入实参。有实参时取实参的值,无实参时取默认值。
2,有默认值的形参必须处于没有默认值的形参后面,即参数列表的最后面。
3,有多个默认值的形参时,想为某个指定的形参传值而别的形参使用默认值,可以在写实参时使用形参名=参数值。如下:
def add(a=5,b=3):
print("a =",a,",b =",b)
add(b=15) # 如果不写b=,则15会传给形参a
要注意的是:形参的名一定要中方法中定义的一样。
问题
当函数被定义时,默认参数的值就会被计算出来,以后每一次使用该函数默认参数都是同一个。也就是说,在整个应用的生命周期中,函数的默认参数只会被计算一次,以后无论调用多少次函数,默认参数都不变,都是第一次计算出来的值。因此,当使用可变参数为默认参数时,就会出现如下问题:
def function(L=[]):
L.append('END')
return L
print function() # ['END']
print function() # ['END', 'END']
这是因为在函数定义时,L指向的列表对象已经确定,所以每调用一次函数后,L指向的列表都会被修改,这就导致了列表中的元素不断地增加。为避免该问题,可以使用None做为默认参数,并且在方法内部显式地为其赋值。如下:
def function(L=None): # 使用None做为默认参数
if L is None: # 每调用该函数时,L都是None
print 'L is None'
L = [] # 每调用该函数,L都被重新赋值为空列表
L.append('END')
return L
总结:禁止使用可变参数做为函数的默认参数。
局部变量与全局变量与global
函数内部是局部变量,其余的为全局变量。如下:
#coding:utf-8
x = "outter"
def fun():
x = 10 # 局部变量,虽然它的变量名与外界的x相同
if x:
print("fun innter",x) # fun innter 10
print("fun",x)
if x:
x = "xxxx"
print("if----",x) # if---- xxxx
fun() # fun 10
print("#"*40)
print(x) # xxxx
可以将整个python代码理解为一个函数,同一个函数内部是可以相互使用、修改,不同的函数内部的是重新定义的,没有办法直接相互影响。如第二个if判断是和x="outer"定义在同一函数中,所以使用的时x就是outer;第一个if判断是和x=10处于同一个函数中的,所以使用时x=10。
global:将一个变量定义为全局变量。如果已有同名的全局变量,则指的就是该全局变量;如果没有定义,就相当于新定义一个全局变量。如:
#coding:utf-8
x = "outter"
def fun():
global y,x
y = "fun"
print(x) # outter
fun()
print(y) # fun
global定义了新全局变量y,所以外界可以使用y。而global又在fun中引用了全局变量x,所以fun中可以使用全局变量x。
参数与元组
元组可以拆解(如a,b=("aaa","bbb")给变量a,b赋值),对于有多个参数的函数时,所以可以将元组拆分赋值给不同的形参。如下:
def fun(a,b):
print("%s=%s" % (a,b))
fun(*(3,5))
*:将list或tuple变成可变参数传入到函数中,或者说将list或tuple拆解传入到函数中。这时不能直接传入一个元组,因为会将元组当作一个具体的参数,而不会将元组进行拆解赋值给两个形参。
%s:与c中%s,%d等表示法完全一致,都表示将参数按相应的类型进行格式化。
%():括号中的参数表示格式化时使用到的数据。也就是说print()函数中的参数完全是和c中一样的,都是将参数进行格式化后输出。如
>>> def f(x,y):
... print("%d-%d=%d" % (x,y,x-y))
...
>>> f(3,5)
3-5=-2
元组中元素的个数必须与函数的参数一致。
参数与字典
与元组类似,只不过传入的时候需要使用**。将dict中的键值对以key=value的形式传入函数中。在python中,可以通过赋值的方式为某一个具体的参数传值。如下:
def function(a,b):
return a**b
print function(b=2,a=3) # 9,为指定的参数传值,而不需要考虑形参的顺序
因此,**dict的作用就是将字典中的key当作形参名,value当作实参传递到函数中。如果函数中有相应的形参名,那该形参就会接收对应的value,如果没有就会报错(除非形参中定义了可变参数)。
字典的Key与参数的参数名一致——名一致、个数一致,但顺序可以不一致。如:
def function(a,b,**c):
for k,v in c.items():
print 'k = %s,v=%s'%(k,v) # k = d,v=dddd
return a**b
p ={'b':3,'a':4,'d':'dddd'}
print function(**p) # 64
有名为a,b的形参,所以p中的a,b对应的value就会传给函数中形参a,b。而没有形参名为d的形参,所以d的value就会存储到可变参数c中。
处理多余的实参
一般来说,函数定义结束后形参的个数已经固定了,但有时候可能传入的实参个数比形参个数多,此时会报错。
可变参数——使用元组存储多余参数
def fun(a,*b): # *表示b是一个元组,用于存储多余的实参
print("%s's age is %s" % (a,b))
fun("zhangsan",34,343) # zhangsan's age is (34, 343)
在定义形参时以*+参数名形式,则该参数代表的就是一个元组,是一个可变参数,调用函数时传入的多余实参就会存储到可变参数中。
上面调用函数时,除了第一个参数传给了形参a,其余两个参数都存储到了元组b中。同样,在调用的时候还可以再多传入一些参数。
关键字参数——**以字典存储多余参数
在传参时,可以为指定的形参赋值(arg=value就是为形参名为arg的形参赋值为value),此时接收多余的参数就需要使用字典,用**表示。如下:
def fun(a,*b,**c): # **表示c是一个字典
print("%s's age is %s and %s" % (a,b,c))
fun("zhangsan",34,343,x=10,name="xxx") # zhangsan's age is (34, 343) and {'name': 'xxx', 'x': 10}
在定义形参时以**+参数名形式定义,则该参数代表的是一个dict,是一个关键字参数,调用函数时传入的多余的key=value形式的实参就会传入到dict中。
可以看出将最后的x=10,name="xxx"全部存储到字典c中。
将字典与元组添加到函数中后,该函数就可以接收任意的参数——无论是个数还是形式。
定义关键字参数
虽然可以通过**kw将用户传入的多余的关键字给保存起来,但这也允许了用户随意传入关键字——有些关键字并不是想要的。因此,可以指定关键字。注意:如果定义了关键字,传参时必须为该关键字传值。有两种方法。
1,如果参数中有可变参数,则在可变参数之后关键字参数之前,定义自己指定的关键字。
2,如果参数中没有可变参数,则在默认参数之后,跟一个*号,并在*之后,关键字参数之前,定义自己指定的关键字。如:
def test(a,b=3,*c,city): # city就是指定的关键字
print(a,b,c,city)
test(3,4,6,'a','b',city='bijing') # 传入参数时,只能传入city,如果写别的就会出错。除非参数中有定义的有关键字参数
上述中有可变参数,所以关键字定义在*c之后。在传参时,除了city='',不能再写别的Key=value。因为没有定义关键字参数。
总结
实参中含有*,**,代表着该实参进行拆解。有相应的形参的就传递给相应的形参,没有的就传递给相应的可变参数,也没有用报错。
形参中含有*,**,代表该形参是可变参数,可以接收多余的实参。
几种参数的顺序为:必选参数(又叫位置参数),有默认值的参数,可变参数,关键字参数。大致为def f(a,b=2,*c,**d)。
lambda表达式
冒号前是参数,多个参数通过逗号隔开,冒号左边是返回值,不需要写return。如:
>>> g=lambda x,y:x**y # lambda表达式
>>> g(2,3)
8