函数是对程序逻辑进行结构化或过程化的一种编程方法。函数与过程相比,两者都是可以被调用的实体,但是传统意义上的函数或者“黑盒”,可能不带任何输入参数,经过一定的处理,最后向调用者传回返回值,而过程是简单、特殊、没有返回值的函数。其实,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