43、函数 (1)若函数没有返回值,则默认的返回值是None (2)装饰器 装饰器实际就是函数,它接受函数对象。我们在执行函数之前,可以运行些预备代码,也可以在执行代码之后做些清理工作。这类似于java中的AOP,即面向切面编程,可以考虑在装饰器中置入通用功能的代码来降低程序复杂度。例如,可以用装饰器来:引入日志、增加计时逻辑来检测性能、给函数加入事务的能力 1. 无参装饰器: @deco2 @deco1 def func(arg1, arg2, ...): pass 这和创建一个组合函数是等价的: def func(arg1, arg2, ...): pass func = deco2(deco1(func)) 2. 有参装饰器: @deco1(deco_arg) @deco2 def func(): pass 这等价于: func = deco1(deco_arg)(deco2(func)) 示例: from time import ctime,sleep def tsfunc(func): def wrappedFunc(): print '[%s] %s() called' % (ctime(), func.__name__) return func() return wrappedFunc @tsfunc def foo(): print 'foo() is invoked !' foo() sleep(4) for i in range(2): sleep(1) foo() 运行结果: [Tue Jul 17 22:45:54 2012] foo() called foo() is invoked ! [Tue Jul 17 22:45:59 2012] foo() called foo() is invoked ! [Tue Jul 17 22:46:00 2012] foo() called foo() is invoked ! (3)传递函数 函数也是phthon对象的一种,也是一个对象,也可以将函数对象的引用赋值给一个变量,通过这个变量,也相当于是这个函数的别名,来调用这个函数,基于这种机制,就有了传递函数,即可以将一个函数名通过形参传递给另外一个函数,在这个函数中调用传递进来的函数。如下示例: >>> def foo(): print 'in foo()' >>> bar=foo >>> bar() in foo() >>> def bar(argfunc): argfunc() >>> bar(foo) in foo() >>> (4)可变长度的参数 于函数调用提供了关键字以及非关键字两种参数类型,python 用两种方法来支持变长参数,在函数调用中使用*和**符号来指定元组和字典的元素作为非关键字以及关键字参数的方法。 1. 非关键字可变长参数(元组) 可变长的参数元组必须在位置和默认参数之后,带元组(或者非关键字可变长参数)的函数普遍的语法如下: def function_name([formal_args,] *vargs_tuple):pass 星号操作符之后的形参将作为元组传递给函数,元组保存了所有传递给函数的"额外"的参数(匹 配了所有位置和具名参数后剩余的)。如果没有给出额外的参数,元组为空。 之前,只要在函数调用时给出不正确的函数参数数目,就会产生一个TypeError异常。通过末尾增加一个可变的参数列表变量,我们就能处理当超出数目的参数被传入函数的情形,因为所有的额外(非关键字)参数会被添加到变量参数元组。 示例: >>> def tupleVarArgs(arg1,arg2='defaultB',*rest): print 'formal arg1: ',arg1 print 'formal arg2: ',arg2 for eachXarg in rest: print 'another arg: ',eachXarg >>> tupleVarArgs('abc') formal arg1: abc formal arg2: defaultB >>> tupleVarArgs('abc',25.0) formal arg1: abc formal arg2: 25.0 >>> tupleVarArgs('abc',25.0,'suo','piao',999999L) formal arg1: abc formal arg2: 25.0 another arg: suo another arg: piao another arg: 999999 精要:多余的参数保存到一个元组中,传递给函数 2.关键字变量参数(Dictionary) 语法:def function_name([formal_args,][*vargst,] **vargsd):pass 示例: >>> def dicVarArgs(arg1,arg2='defaultB',**rest): print 'formal arg1: ',arg1 print 'formal arg2: ',arg2 for key in rest: print 'xtra arg %s: %s ' % (key,rest[key]) >>> dicVarArgs('abc') formal arg1: abc formal arg2: defaultB >>> dicVarArgs('abc',123) formal arg1: abc formal arg2: 123 >>> dicVarArgs('abc',123,c='suo',d=456,e=9999L) formal arg1: abc formal arg2: 123 xtra arg c: suo xtra arg e: 9999 xtra arg d: 456 >>> 精要:多余的关键字参数,将其放到字典中,传递给函数 关键字和非关键字可变长参数都有可能用在同一个函数中,只要关键字字典是最后一个参数并且非关键字元组先于它之前出现,如下示例: >>> def varArgs(arg1,arg2='defaultB',*tupleRest,**dicRest): print 'formal arg1: ',arg1 print 'formal arg2: ',arg2 for each in tupleRest: print 'non-key arg: ',each for key in dicRest: print 'xtra arg %s: %s ' % (key,dicRest[key]) >>> varArgs('abc') formal arg1: abc formal arg2: defaultB >>> varArgs('abc',123) formal arg1: abc formal arg2: 123 >>> varArgs('abc',123,'suo',456,c='piao',d='love',e=9999) formal arg1: abc formal arg2: 123 non-key arg: suo non-key arg: 456 xtra arg c: piao xtra arg e: 9999 xtra arg d: love 其实,也可以这样来调用带有可变长参数的函数: >>> mytuple=('suo','love','piao',9999) >>> mydic={'c':'how','d':'are','e':'you','f':88888L} >>> varArgs('abc',123,*mytuple,**mydic) formal arg1: abc formal arg2: 123 non-key arg: suo non-key arg: love non-key arg: piao non-key arg: 9999 xtra arg c: how xtra arg e: you xtra arg d: are xtra arg f: 88888 >>> 这样的调用,更加清晰一些。 (5)函数式编程 1. lambda 匿名函数 python 允许用lambda 关键字创造匿名函数,语法为: lambda [arg1[, arg2, ... argN]]: expression lambda必须放在一行中,就像是单行版的函数一样,但应该叫它lambda表达式 它返回一个函数对象,这个函数执行的操作,就是expression中的内容 示例: >>> a=lambda x,y=2:x+y >>> a(4) 6 >>> b=lambda *z : z >>> b(1,2,3) (1, 2, 3) 虽然看起来lambdda 是一个函数的单行版本,但是它不等同于c++的内联语句,这种语句的目的是由于性能的原因,在调用时绕过函数的栈分配。lambda 表达式运作起来就像一个函数,当被调用时,创建一个框架对象。 2.内建函数 apply(func[, nkw][, kw]): 用可选的参数来调用func,nkw 为非关键字参数,kw 关键字参数;返回值是函数调用的返回值。 filter(func, seq): 调用一个布尔函数func 来迭代遍历每个seq 中的元素; 返回一个使func 返回值为ture 的元素的序列 map(func, seq1[,seq2...]): 将函数func 作用于给定序列(s)的每个元素,并用一个列表来提供返回值;如果func 为None, func 表现为一个身份函数,返回一个含有每个序列中元素集合的n 个元组的列表。 reduce(func, seq[, init]): 将二元函数作用于seq 序列的元素,每次携带一对(先前的结果以及下一个序列元素),连续的将现有的结果和下雨给值作用在获得的随后的结果上,最后减少我们的序列为一个单一的返回值;如果初始值init 给定,第一个比较会是init 和第一个序列元素而不是序列的头两个元素。 (6)变量作用域 当搜索一个标识符的时候,python 先从局部作用域开始搜索。如果在局部作用域内没有找到那 个名字,那么就一定会在全局域找到这个变量否则就会被抛出NameError 异常。 问题引出: 在函数中定义了一个局部的bar变量,在主函数体中,定义了一个全局的bar变量,但是有可能在函数体内的bar变量会覆盖掉全局的bar变量,这样全局的bar变量在函数体内就失效了。。 如下示例: bar=100 def foo(): print 'calling foo()...' bar=200 print 'in foo(), bar is ', bar print 'bar=',bar foo() print 'bar=',bar 运行结果如下: bar= 100 calling foo()... in foo(), bar is 200 bar= 100 问题解决: 为了解决上面的问题,我们可以使用global关键字,明确的引用一个已命名的全局变量 bar=100 def foo(): print 'calling foo()...' global bar bar=200 print 'in foo(), bar is ', bar print 'bar=',bar foo() print 'bar=',bar 运行结果如下: bar= 100 calling foo()... in foo(), bar is 200 bar= 200 (7)递归 求阶乘: >>> def factorial(n): if n==0 or n==1: return 1 else: return (n*factorial(n-1)) >>> factorial(5) 120 (8)生成器,暂时跳过