函数是对程序逻辑进行结构化或过程化的一种编程方法。函数与过程相比,两者都是可以被调用的实体,但是传统意义上的函数或者“黑盒”,可能不带任何输入参数,经过一定的处理,最后向调用者传回返回值,而过程是简单、特殊、没有返回值的函数。其实,python的过程就是函数,因为在函数中如果没有显式return返回值的话,解释器会隐式地返回默认值None。
1、函数定义
def foo([argument-list]): “function_document_string” foo_suite
函数定义使用关键字def,随后是函数名,紧接着圆括号内是个可选的参数列表,参数类型是动态的,不需写明,只需写出参数名即可,然后是函数体(包括了一个虽然可选但是强烈推荐的文档字串,在函数声明后第一个没有赋值的字串;当然也可以在函数创建完成后,使用__doc__在函数外部添加函数文档字串),return语句可选,默认返回None,当返回多个对象时,其实是一个元组。
在其它语言中,像C/C++,函数声明和定义一般是分开的,但python函数将这两者视为一体,且支持前向引用。
python支持内嵌函数,即在函数内部在def定义一个函数,这样的函数便是一个闭包。内嵌函数使用外部函数的局部变量而非全局变量时,这个变量便是自由变量,可通过函数的func_closure属性访问。
函数体内的局部变量如果与函数体外的全局变量同名,这时局部变量用global关键字修饰后,就会覆盖掉全局变量,否则全局变量还是原来的值。
python函数支持递归调用,即自己调用自己。
2、函数调用
函数调用通过操作符-圆括号()完成,还可以把函数名赋值给另一个变量,这时通过新的变量也可以调用函数。
foo() result = foo() foo2 = foo foo2() result2 = foo2()
3、函数参数
调用函数时,参数顺序按照函数声明中的参数顺序输入,如果不按照这个规则,那么参数必须通过参数名进行赋值,这个是函数参数的位置要求。
像C/C++一样,python函数也支持默认参数。
函数名本身也可以作为参数传入一个函数,与其它参数不同的是,函数是可以调用的。
下面是一个默认参数和参数为函数名的例子:grabWeb.py
这个脚本从Baidu主页抓取HTML的第一个和最后一个非空行。
#!/usr/bin/env python from urllib import urlretrieve def firstNonBlank(lines): for eachLine in lines: if not eachLine.strip(): continue else: return eachLine def firstLast(webpage): f = open(webpage) lines = f.readlines() f.close() print firstNonBlank(lines), lines.reverse() print firstNonBlank(lines), def download(url = 'http://www.baidu.com', process = firstLast): try: retval = urlretrieve(url)[0] except IOError: retval = None if retval: print retval process(retval) if __name__ == '__main__': download()
函数的参数组——
python允许执行一个没有显式定义参数的函数,方法是把元组(非关键字参数)或字典(关键字参数)作为参数组传递给函数。
func(positional_args,keyword_args, *tuple_grp_nonkw_args, **dict_grp_kw_args)
其中,tuple_grp_nonkw_args(注意前面的*)是以元组形式体现的非关键字参数组,dict_grp_kw_args(注意前面的**)是装有关键字参数的字典,还包括位置参数和关键字参数,该语法中所有的参数都是可选的。这种参数组可以有效地取代apply()内建函数。
下面是使用参数组的一个例子:mathGame.py
随机选择数字以及一个算术函数,显示问题,以及验证结果,在3次错误的尝试以后给出结果,等待用户输入一个正确的答案后便会继续运行。
#!/usr/bin/env python
from operator import add, sub
from random import randint, choice
ops = {'+': add, '-': sub}
MAXTRIES = 2
def doprob():
op = choice('+-')
nums = [randint(1, 10) for i in range(2)]
nums.sort(reverse = True)
ans = ops[op](*nums)
pr = '%d %s %d = ' %(nums[0], op, nums[1])
oops = 0
while True:
try:
if int(raw_input(pr)) == ans:
print 'correct'
break
if oops == MAXTRIES:
print 'answer\n%s%d' %(pr, ans)
else:
print 'incorrect... try again'
oops += 1
except (KeyboardInterrupt, EOFError, ValueError):
print 'invalid input... try again'
def main():
while True:
doprob()
try:
opt = raw_input('Again? [y]').lower()
if opt and opt[0] == 'n':
break
except (KeyboardInterrupt, EOFError):
break
if __name__ == '__main__':
main()
可变长参数在python中也是支持的,对应于上面提到的函数参数组,非关键字可变长参数为一个元组,用*表示,关键字可变长参数为一个字典,用**(不是冪运算,已重载)表示,在函数参数列表中的顺序是普通位置参数、默认参数、非关键字可变长参数*、关键字可变长参数**,如下例子:
def function(arg, arg2 = 'default', *aTuple, **aDict): pass
4、函数装饰器@
装饰器是在函数调用之上的修饰,语法以@开头,接着是装饰器名字和可选的参数,紧跟着装饰器的便是被修饰的函数。装饰器可以如函数调用一样对叠起来,下面是一个普遍的例子:
@decorator2(deco_arg) @decorator1 def function(): pass
上面的两个装饰器,一个不带参数,一个带参数,等价于下面的用法:
def function(): pass function = decorator2(deco_arg)(decrator1(function))
从本质上看,装饰器引入了java开发者称呼之为AOP面向方面编程的概念,可以用装饰器来引入日志、增加计时逻辑来检测功能、给函数加入事务的能力等通用功能。
下面是一个使用了装饰器的例子:decoratorEx.py
这个装饰器有个内嵌函数(闭包),用于显示函数执行时间,是一个时戳装饰。
#!/usr/bin/env python from time import ctime, sleep def tsfunc(func): def wrappedFunc(): print '[%s] %s() called' %(ctime(), func.__name__) return func() return wrappedFunc @tsfunc def foo(): pass if __name__ == '__main__': foo() sleep(2) for i in range(2): sleep(1) foo()
执行结果如下(时间与代码意图吻合):
[Fri May 29 10:50:50 2015] foo() called [Fri May 29 10:50:53 2015] foo() called [Fri May 29 10:50:54 2015] foo() called
上面的例子使用了闭包和装饰器,比较简单,下面展示一个复杂的例子,给装饰器添加参数,该参数决定哪一个闭包会被调用,也就是说,在函数调用前还是调用后执行装饰内容。
#!/usr/bin/env python
from time import time
def logged(when):
def log(f, *args, **kargs):
print '''Called:
function: %s
args: %r
kargs: %r''' %(f, args, kargs)
def pre_logged(f):
def wrapper(*args, **kargs):
log(f, *args, **kargs)
return f(*args, **kargs)
return wrapper
def post_logged(f):
def wrapper(*args, **kargs):
now = time()
try:
return f(*args, **kargs)
finally:
log(f, *args, **kargs)
print "time delta: %s" %(time() - now)
return wrapper
try:
return {"pre": pre_logged, "post": post_logged}[when]
except KeyError, e:
raise ValueError(e), 'must be "pre" or "post"'
@logged("post")
def post_hello(name):
print "Hello, ", name
@logged("pre")
def pre_hello(name):
print "Hello, ", name
if __name__ == '__main__':
pre_hello("World!")
print '-' * 50
post_hello("Python!")
执行结果如下:
Called:
function: <function pre_hello at 0x7f47cfff17d0>
args: ('World!',)
kargs: {}
Hello, World!
--------------------------------------------------
Hello, Python!
Called:
function: <function post_hello at 0x7f47cfff15f0>
args: ('Python!',)
kargs: {}
time delta: 9.89437103271e-05
5、lambda表达式
lambda表达式创造匿名函数,返回可调用的函数对象,语法如下:
labmbda [arguments]: expression
6、函数式编程的几个内建函数
apply(func[, nkw][, kw]):用可选的参数来调用func,nkw为非关键字参数,kw为关键字参数,返回值是函数调用的返回值。这个函数在其后的python版本中逐渐被淘汰。
filter(func, seq):调用一个布尔函数func来迭代遍历每个seq中的元素,返回一个使func返回值为true的元素的列表。
map(func, seq1[, seq2...]):将函数func作用于给定序列的每个元素,并用一个列表来提供返回值,如果func为None,func表现为一个身份函数,返回一个含有每个序列中元素集合的n个元组的列表。
reduce(func, seq[, init]):将二元函数作用于seq序列的元素,每次携带一对(先前的结果以及下一个序列元素),连续的将现有的结果和下一个值作用在获得的随后的结果上,最后减少我们的序列为一个单一的返回值。如果初始值init给定,第一个比较会是init和第一个序列元素而不是序列的头两个元素。
以上四个函数常常和lambda表达式一起使用,其func参数设置为一个lambda表达式。
7、偏函数应用
偏函数应用PFA把函数式编程、默认参数和可变参数结合在了一起,可通过functools模块中的partial()函数来创建。
>>> from operator import add >>> from functools import partial >>> add1 = partial(add, 1) >>> add1(10) 11 >>> add1(100) 101
上面的add1()接收一个参数,和默认固定的1相加,add1(x)等价于add(1,x)。
原文地址:http://blog.csdn.net/ieearth/article/details/46239363