43、函数(1)若函数没有返回值,则默认的返回值是None(2)装饰器装饰器实际就是函数,它接受函数对象。我们在执行函数之前,可以运行些预备代码,也可以在执行代码之后做些清
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)生成器,暂时跳过