1 、为什么需要函数
函数是一个程序语言的基本功能,函数主要用来封装一个代码块,从而对部分的代码进行封装,不用在写程序的时候,各种粘贴复制。
函数和过程都是可以调用的,但是函数与过程不同的是函数一定会有一个返回值,如果没有显式的使用return语句返回值,那么返回的是一个空对象,也就是None,但是过程是没有返回值的。
2、函数的定义,调用,返回值
在函数的定义的时候,只要使用def关键字进行定义,然后是函数的名称,在是小括号来进行定义,如下所示:
>>> def func1(x):(def是定义函数的关键字,func1表示函数的名称,x表示函数的参数)
... print"this is a function"
...
在调用函数的时候,直接使用函数名加括号即可,有参数的时候,添加相关的参数:
>>> func1('some')(调用函数)
this is a function
在python的函数中,可以返回值为None,也可以返回一个对象,也可以返回多个对象,当返回多个对象的时候,其实返回的是一个元祖,内部是将多个对象进行组装,放在一个元祖中,从而,可以认为是返回多个对象,如下所示:
>>> def getlogpath(): (函数没有返回值)
... print"return nothing"
...
>>> values = getlogpath() (当函数没有使用return返回值的时候,那么返回的是一个None类型的对象)
return nothing
>>> type(values)
<type 'NoneType'>
>>> def getlogpath(): (函数返回一个对象,在这里返回的是一个字符串对象)
... return"one object"
...
>>> values = getlogpath()
>>> type(values)
<type 'str'>
>>> print values
one object
>>> def getlogpath(): (函数返回多个对象,在返回的时候用逗号进行分割,从而返回多个对象)
... return"one object","another object"
...
>>> values = getlogpath()
>>> type(values) (返回多个对象的时候,那么其实返回的是多个对象组成的元祖)
<type 'tuple'>
>>> def getlogpath(): (定义一个函数,返回一个列表)
... import os
... returnos.listdir('/tmp')
...
>>> values = getlogpath()
>>> type(values) (返回值为一个对象,也就是列表对象)
<type 'list'>
>>> values (取得的值为一个列表)
['kel.py', '.ICE-unix']
3、函数的调用
在函数进行调用的时候,使用的是小括号进行调用,当函数具有参数的时候,那么调用方式可以分为几种。
标准调用也可以称之为位置调用,也就是按照参数的位置来进行调用函数,如下所示:
>>> def getFile(x,y):(定义个函数,具有两个参数,返回值为None)
... print x
... print y
...
>>> getFile(3,4)(使用位置参数来调用函数,调用函数的时候,按照位置来进行赋值,也就是x赋值为3,y赋值为4)
3
4
关键字参数调用,主要是通过函数调用中的参数来进行区分参数,当使用关键字参数进行调用函数的时候,参数可以不按照顺序进行调用,如下所示:
>>> m,n=3,4
>>> getFile(m,n)(位置调用函数,变量按照传递的参数挨个进行赋值)
3
4
>>> getFile(x=m,y=n)(关键字调用参数,根据函数定义中参数来进行赋值)
3
4
>>> getFile(y=n,x=m)(关键字调用参数的时候,顺序随意,在赋值的时候会按照关键字进行赋值)
3
4
在进行函数调用的时候,如果没有使用可变参数定义函数,那么调用函数的时候,参数的个数必须是明确对应的,也就是定义的时候函数的参数个数和调用的时候参数的个数必须相等,多或者少都会报错,如下所示:
>>> getFile("1")(参数格式不匹配,在定义函数的时候,定义了两个参数,而调用的时候,只给了一个参数)
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
TypeError: getFile() takes exactly2 arguments (1 given)
>>> getFile(1,2,3) (参数格式不匹配,在定义函数的时候,定义了两个参数,而调用的时候,给了三个参数,)
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
TypeError: getFile() takes exactly2 arguments (3 given)
当函数在定义的时候使用了默认参数的时候,那么允许参数的个数不同,在参数不同的时候,那么就会将未赋值的参数设置为默认参数,如下所示:
>>> defgetFile(filename,filepath="/var/log"):(定义函数,其中一个参数设置了默认参数)
... printfilename
... printfilepath
...
>>> getFile("kel.log")(当函数定义中有默认参数的时候,如果没有传入对应的参数,那么会使用默认值)
kel.log
/var/log
>>> getFile("kel.log","log")(位置调用,当传入对应的参数的时候,那么就不会使用默认值,使用的是传入的参数的值)
kel.log
log
>>> getFile(filepath ="somepath",filename = "filename")(关键字调用,顺序随意,覆盖默认值参数)
filename
somepath
>>> getFile()(当函数定义的时候使用了默认值参数,那么调用的时候必须传入未设定默认值的参数个数,报错中显示,至少有一个参数必须传入)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError:getFile() takes at least 1 argument (0 given)
>>> defgeFile(filepath="/var/log",filename):(在定义函数的时候,有默认值的参数必须在没有默认值参数的后面)
... printfilepath
... printfilename
...
File"<stdin>", line 1
SyntaxError: non-default argumentfollows default argument
在调用函数的时候,还可以使用参数组来进行调用,如下所示:
>>> def getFile(x,y,z):
... print x
... print y
... print z
...
>>> alist=[1,2,3]
>>> getFile(*alist)(在调用的时候,使用*,表示对参数进行解包,从而对各个参数进行赋值)
1
2
3
>>> atuple=(1,2,3)(对list和tuple都可以进行unpack,从而对参数赋值,相当于位置参数)
>>> getFile(*atuple)
1
2
3
>>>adict={"x":1,"y":2,"z":3}(对字典相当于使用关键字参数的调用,必须key和对应的函数参数完全一致)
>>> getFile(*adict)
y
x
z
>>> getFile(**adict)(也可以直接使用字典,使用双**号,表示使用关键字参数来进行调用)
1
2
3
从而在进行函数调用的时候,可以调用的方式如下所示:
func(positional_args,keyword_args,*tuple_grp_args,*dict_grp_args)
在调用函数的,如果调用的参数是不可变对象,例如元祖,字符串,数字,那么原来的变量不会发生变化,但是如果是传递的参数是可变对象,那么这个值可能会发生变化,如下所示:
>>> m=3
>>> def get(x):(定义一个函数,对参数进行修改,不可变对象不会对原来的变量产生影响)
... x ="some"
... print x
...
>>> get(m)
some
>>> print m
3
>>> alist = [1,2,3]
>>> def get(x):(传递可变对象list,在函数中修改list,那么原变量会发生改变)
... x.pop()
...
...
>>> get(alist)
>>> alist
[1, 2]
4、函数的定义
在进行函数定义的时候,有的时候,如果函数没有定义,那么在其他函数体中进行调用,那么会出现错误,但python只要定义了这个函数即可,可以在前也可以在后,也就是python在开始编译的时候,只是定义了一个函数对象,而没有进行真正的调用,如下所示:
>>> def foo():(定义函数foo,在函数内部调用函数bar,虽然未定义bar,但是编译不会出错)
... bar()
... print"foo"
...
>>> foo()(调用函数的时候,才会真正执行bar函数,从而会出现错误)
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
File"<stdin>", line 2, in foo
NameError: globalname 'bar' is not defined
>>> def bar():(再定义bar函数)
... print"bar"
...
>>> foo()(再次调用,成功执行,也就是函数定义的顺序无所谓)
bar
foo
在定义函数的时候,可以嵌套的定义函数,如下所示:
>>> def f1():(在函数f1内部定义一个内嵌函数f2,由于命名空间的关系,只有在函数f1内部才能调用函数f2,从而在调用函数f1的时候,返回一个f2的函数对象)
... x=3
... def f2():
... y=4
... print y
... return f2
...
>>> f1()(调用f1函数,返回的是一个f2函数的对象)
<function f2 at 0x7fb256f8b140>
>>> i=f1()
>>> type(i)(类型为函数)
<type 'function'>
>>> i()(再次调用,也就是调用了内嵌的函数f2)
4
>>> f1()()(也可以如此调用)
4
在定义函数的时候,主要是上面讲述的到的位置参数,关键字参数,默认参数,还有两个就是可变长度的参数,当使用可变长度参数的时候,位置参数将被元祖接收,关键字参数将被字典接收,如下所示:
>>> alist = [1,2,3]
>>> adict = {1:1,2:2,3:3}
>>> f(*alist,**adict)(在使用字典的时候,字典的键必须为字符串,也就是关键字参数必须是字符串)
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
TypeError: f() keywords must be strings
>>> adict={"x":1,"y":2}
>>> f(*alist,**adict) (进行调用,位置参数会被元祖接收,字典参数会被字典接收)
<type 'tuple'> (1, 2, 3)
<type 'dict'> {'y': 2, 'x': 1}
>>> f(alist,adict) (在不解包的情况下,传过去的参数会被当成位置参数,从而被位置参数接收,并且第一个元素为列表,第二个元素为字典)
<type 'tuple'> ([1, 2, 3], {'y': 2, 'x': 1})
<type 'dict'> {}
>>> f(1,2,3,x=1,y=2)(使用位置参数和关键字参数进行调用,那么会自动进行将位置参数作为元祖传入,将关键字参数作为字典传入)
<type 'tuple'> (1, 2, 3)
<type 'dict'> {'y': 2, 'x': 1}
5、变量的作用域
在python中,每个变量都会有其生存的作用域,一般的查找变量的时候,先找到局部变量,当局部变量没有的时候,就找上层函数的变量,当上层函数没有的时候,就会找全局变量,如果全局变量没有,那么就查找内建变量,如果再没有,那么就会出现报错异常,如下:
[root@python tmp]# cat kel.py
#!/usr/bin/env python
z = "gloabal" (全局变量)
def f1():
x ="function var"(函数内变量)
print x,z
def f2():
y ="local var" (局域变量)
printx,y,z
return f2
f1()()
[root@python tmp]# python kel.py (局域变量没找到的时候,那么就会到上级变量中查找)
function var gloabal
function var local var gloabal
[root@python tmp]# python kel.py (当未找到对应的变量的时候,就显示名称错误,全局名称未定义)
function var
Traceback (most recent call last):
File"kel.py", line 11, in <module>
f1()()
File"kel.py", line 5, in f1
print x,z
NameError: global name 'z' is not defined
[root@python tmp]# cat kel.py (在内部函数或者本地变量中修改全局变量的值,必须使用关键字global来进行声明,否则无法修改)
#!/usr/bin/env python
x = "global"
z = "gloabal"
def f1():
global x
x ="function var"
print x,z
def f2():
y ="local var"
printx,y,z
return f2
f1()()
print x
[root@python tmp]# python kel.py (查看修改全局变量的结果)
function var gloabal
function var local var gloabal
function var
6、匿名函数
在python,匿名函数也就是使用lambda定义的函数,主要用途就是当函数的逻辑很简单的时候,那么就可以使用匿名函数,如下所示:
>>> l = lambda x,y:x+y (匿名函数的定义,将函数赋值给一个变量)
>>> type(l) (变量类型为函数,从而是可以调用的)
<type 'function'>
>>> l(3,4)(调用匿名函数,参数也就是位置参数x和y,也可以使用默认参数)
7
>>> def l(x,y):(匿名函数相当于如下的函数定义)
... return x+y
...
>>> l(3,4)
7
>>> x = 10(全局变量)
>>> def f():
... y=3 (局部变量)
... bar =lambda :x+ y (lambda匿名函数)
... returnbar() (返回匿名函数的值)
...
>>> f()(调用函数)
13
7、函数式编程
函数式编程是一种编程范式,其实主要作用就是将一个函数作为参数传递给另外一个函数,在python中,主要的几个函数式编程的内建函数为filter,map,reduce,如下所示:
>>> def filterfunc(x): (定义filter将要使用的函数,满足条件的就返回True,不满足条件的False)
... if x>2:
... returnTrue
... returnFalse
...
>>> l1 = [-1,1,2,1.1,3,45](定义序列)
>>> filter(filterfunc,l1) (调用filter,传入的参数为一个是函数,一个是一个序列,最后生成一个新的序列)
[3, 45]
filter函数相当于一个过滤器,满足条件的返回,不满足条件的移除。
>>> def mapfunc(x,y): (定义处理的函数,map表示映射)
... returnx*2,y*3
...
>>> l1
[-1, 1, 2, 1.1, 3, 45]
>>> )
>>> l2 = [1,2,3,4,5]
>>> map(None,l1,l2)(当不需要对序列进行处理的时候,函数使用None,当长度不一致的时候,会显示为None)
[(-1, 1), (1, 2), (2, 3), (1.1, 4), (3, 5), (45,None)]
>>> map(mapfunc,l1,l2) (当长度不一致的时候,可能会发生错误)
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
File"<stdin>", line 2, in mapfunc
TypeError: unsupported operand type(s) for *:'NoneType' and 'int'
>>> l1= [7,8,6,5,4]
>>> map(mapfunc,l1,l2) (返回一个列表,将多个序列进行操作后的结果返回)
[(14, 3), (16, 6), (12, 9), (10, 12), (8, 15)]
>>> def mapfunc(x,y,z):(使用map函数的时候,自定义函数的参数必须和序列的个数相同,否则会出现错误,可以返回多个元素,也可以只返回一个元素)
... returnx+y+z
...
>>> l1 = [1,2,3]
>>> l2 = [3,4,5]
>>> l3 = [4,5,6]
>>> map(mapfunc,l1,l2,l3)(返回一个列表,对序列进行了整合)
[8, 11, 14]
>>> def reducefunc(x,y):(定义reduce的处理函数,reduce的处理函数的参数必然是两个,都是序列中相邻的两个值)
... return x+y
...
>>> l1
[1, 2, 3]
>>> reduce(reducefunc,l1)(对序列l1进行求和)
6
>>> reduce(reducefunc,l1,10)(对序列l1进行求和,初始值为10)
16
>>> reduce(reducefunc,l1,11) (对序列l1进行求和,初始值为11)
17
如下例子,取出/etc/passwd中以/bin/bash结尾的用户的列表:
>>> def getbinbash(x):(filter的判断函数,判断哪个用户是以/bin/bash结尾)
... ifx.strip().endswith('/bin/bash'):
... returnTrue
... returnFalse
...
>>>filter(getbinbash,open('/etc/passwd').readlines())(取出以/bin/bash结尾的行)
['root:x:0:0:root:/root:/bin/bash\n','mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash\n']
>>> map(lambdax:x.split(":")[0],filter(getbinbash,open('/etc/passwd').readlines()))(使用map取出用户名)
['root', 'mysql']
8、生成器
在生成器表达式中,返回的是一个生成器对象,当定义一个函数的时候,如果具有yield关键字,那么这个函数也返回一个生成器对象,如下所示:
>>> def genNum(x):(定义一个函数,使用yield关键字,使得函数返回一个生成器对象)
... y = 0
... while y< x:
... yield y
... y += 1
...
>>> g1 = genNum(10)
>>> type(g1) (返回的类型为一个生成器)
<type 'generator'>
>>> g1.next() (生成器对象特殊的迭代器,从而也具有next方法)
0
>>> for i in g1: (生成器对象是可迭代的,从而可以使用for来进行遍历)
... print i
...
1
2
3
4
5
6
7
8
9
9、装饰器
装饰器本身也是一个函数,用于增强其他函数的功能,装饰器是闭包的高级应用。
装饰器一般接收一个函数作为参数,从而来增强这个函数的功能。
>>> def deco(func):(定义一个装饰器,装饰器以一个函数为参数)
... defwrapper(): (内嵌函数,调用的函数没有参数)
... print"start..." (增强功能,做一些其他的事情)
... func() (调用函数)
... print"end ..."
... returnwrapper (返回内部函数对象)
...
>>> @deco (表示使用装饰器)
... def printf(): (原来的函数)
... print"do something"
...
>>> printInfo() (调用函数,函数的功能被增强)
start...
do something
end ...
10、递归
递归表示调用自身,当有一个边界条件满足之后,那么就结束调用,如下所示:
>>> def f(n): (定义递归函数)
... if n == 1:(边界条件,结束递归调用)
... return n
... else:
... returnn+f(n-1) (递归调用自身)
...
>>> f(10)(求1到10的和)
55
QQ群:40086757,可以进入交流