码迷,mamicode.com
首页 > 编程语言 > 详细

python函数及函数式编程

时间:2015-05-29 23:17:01      阅读:146      评论:0      收藏:0      [点我收藏+]

标签:python   python函数   

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

5lambda表达式

lambda表达式创造匿名函数,返回可调用的函数对象,语法如下:

labmbda [arguments]: expression

6、函数式编程的几个内建函数

apply(func[, nkw][, kw]):用可选的参数来调用funcnkw为非关键字参数,kw为关键字参数,返回值是函数调用的返回值。这个函数在其后的python版本中逐渐被淘汰。

filter(func, seq):调用一个布尔函数func来迭代遍历每个seq中的元素,返回一个使func返回值为true的元素的列表。

map(func, seq1[, seq2...]):将函数func作用于给定序列的每个元素,并用一个列表来提供返回值,如果funcNonefunc表现为一个身份函数,返回一个含有每个序列中元素集合的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)



python函数及函数式编程

标签:python   python函数   

原文地址:http://blog.csdn.net/ieearth/article/details/46239363

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!