第11章 函数和函数式编程
一 调用函数
1 关键字参数
def foo(x):
foo_suite # presumably does some processing with 'x'
标准调用 foo(): foo(42) foo('bar') foo(y)
关键字调用 foo(): foo(x=42) foo(x='bar') foo(x=y) 即明确给出相应的参数名
2 参数组
Python允许程序员执行一个没有显式定义参数的函数,相应的方法是通过一个把元组(非关键字参数)或字典(关键字参数)作为参数组传递给函数。
func( *tuple_grp_nonkw_args, **dict_grp_kw_args )
3 小结
python 中允许的函数调用的完整语法为:
func( positional_args, keyword_args, *tuple_grp_nonkw_args, **dict_grp_kw_args )
二 创建函数
1 def语句
def function_name(arguments):
"function_documentation_string"
function_body_suite
特别注意:python不区分函数的声明与定义!
2 函数属性
函数也可以有用.号表示的属性
3 内部函数
在函数体内创建另外一个函数:
def foo():
def bar():
print 'bar() called'
bar() #bar()属于函数foo的内部函数,因此只能在foo函数体内部进行调用!!!
4 传递函数
python的函数和普通对象一样,可以被传递
举例:
>>> def foo():
... print 'in foo()'
...
>>> bar = foo 函数传递!
>>> bar()
in foo()
>>> def bar(argfunc):
... argfunc()
...
>>> bar(foo) 函数传递!
in foo()
三 函数参数
1 形式参数
(1)位置参数: 必须以在被调用函数中定义的准确顺序来传递
(2)默认参数
2 可变长度参数
(1)非关键字可变参数(元组)
def function_name([formal_args,] *vargs_tuple): #所有形式参数必须先于非正式的参数之前出现!!!
"function_documentation_string"
function_body_suite
举例:
def tupleVarArgs(arg1, arg2='defaultB', *theRest):
print 'formal arg 1:', arg1
print 'formal arg 2:', arg2
for eachXtrArg in theRest:
print 'another arg:', eachXtrArg
>>> tupleVarArgs('abc', 123, 'xyz', 456.789)
formal arg 1: abc
formal arg 2: 123
another arg: xyz
another arg: 456.789
(2)关键字变量参数(字典)
def function_name([formal_args,][*vargst,] **vargsd):
function_documentation_string function_body_suite
举例:
def dictVarArgs(arg1, arg2='defaultB', **theRest):
print 'formal arg1:', arg1
print 'formal arg2:', arg2
for eachXtrArg in theRest.keys():
print 'Xtra arg %s: %s' % \
(eachXtrArg, str(theRest[eachXtrArg]))
>>> dictVarArgs('one', d=10, e='zoo', men=('freud', 'gaudi'))
formal arg1: one
formal arg2: defaultB
Xtra arg men: ('freud', 'gaudi')
Xtra arg d: 10
Xtra arg e: zoo
(3)关键字和非关键字可变长参数同时出现的情形
要求:关键字字典是最后一个参数并 且非关键字元组先于它之前出现
def newfoo(arg1, arg2, *nkw, **kw):
print 'arg1 is:', arg1
print 'arg2 is:', arg2
for eachNKW in nkw:
print 'additional non-keyword arg:', eachNKW
for eachKW in kw.keys():
print "additional keyword arg '%s': %s" % \
(eachKW, kw[eachKW])
>>> newfoo('wolf', 3, 'projects', freud=90, gamble=96)
arg1 is: wolf arg2 is: 3
additional non-keyword arg: projects
additional keyword arg 'freud': 90
additional keyword arg 'gamble': 96
(4)更多举例
>>> newfoo(2, 4, *(6, 8), **{'foo': 10, 'bar': 12})
arg1 is: 2
arg2 is: 4
additional non-keyword arg: 6
additional non-keyword arg: 8
additional keyword arg 'foo': 10
additional keyword arg 'bar': 12
>>> aTuple = (6, 7, 8)
>>> aDict = {'z': 9}
>>> newfoo(1, 2, 3, x=4, y=5, *aTuple, **aDict)
arg1 is: 1
arg2 is: 2
additional non-keyword arg: 3
additional non-keyword arg: 6
additional non-keyword arg: 7
additional non-keyword arg: 8
additional keyword arg 'z': 9
additional keyword arg 'x': 4
additional keyword arg 'y': 5
四 函数式编程
1 匿名函数与lambda
python 允许用 lambda 关键字创造匿名函数。匿名是因为不需要以标准的def方式来声明。
lambda [arg1[, arg2, ... argN]]: expression
举例:
def add(x, y): return x + y 等价于 lambda x, y: x + y
def usuallyAdd2(x, y=2): return x+y 等价于 lambda x, y=2: x+y
def showAllAsTuple(*z): return z 等价于 lambda *z: z
>>>a = lambda x, y=2: x + y
>>>a(3)
5
>>>b = lambda *z: z
>>> b(23, 'zyx')
(23, 'zyx')
>>> b(42)
(42,)
2 内建函数apply()、filter()、map()、reduce()
lambda 函数可以很好的和这使用了这四个函数的应用程序结合起来,因为它们都带了一个可执行的函数对象,lambda 表达式提供了迅速创造这些函数的机制
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 和第一个序列元素而不 是序列的头两个元素。
举例:
filter():
from random import randint
def odd(n):
return n % 2
allNums = []
for eachNum in range(9):
allNums.append(randint(1, 99))
print filter( odd, allNums )
改变1:用一个 lambda 表达式替换
from random import randint
allNums = []
for eachNum in range(9):
allNums.append(randint(1, 99))
print filter( lambda n: n%2, allNums ) #使用了lambda表达式!!!
改变2:list 综合使用如何能成为 filter()合适的替代者
from random import randint
allNums = []
for eachNum in range(9):
allNums.append(randint(1, 99))
print [n for n in allNums if n%2]
改变3:利用列表解析!
from random import randint as ri
print [n for n in [ri(1,99) for i in range(9)] if n%2]
map():
map( (lambda x: x+2), [0, 1, 2, 3, 4, 5] )
带有多个序列的map()的例子:
>>> map( lambda x, y: x + y, [1,3,5], [2,4,6])
[3, 7, 11]
>>>
>>> map( lambda x, y: (x+y, x-y), [1,3,5], [2,4,6] )
[(3, -1), (7, -1), (11, -1)]
>>>
>>> map( None, [1,3,5], [2,4,6] )
[(1, 2), (3, 4), (5, 6)]
reduce():
>>> print 'the total is:', reduce( (lambda x,y: x+y), range(5) )
the total is: 10 #0+1+2+3+4=10
3 偏函数应用(PFA)
这种函数将任意数量(顺序)的参数的函数转化成另一个带剩余参数的函数对象
例子:
>>> from operator import add, mul
>>> from functools import partial
>>> add1 = partial(add, 1) # add1(x) == add(1, x)
>>> mul100 = partial(mul, 100) # mul100(x) == mul(100, x)
>>>
>>> add1(1)
2
>>> mul100(10)
1000
>>> baseTwo = partial(int, base=2)
>>> baseTwo.__doc__ = 'Convert base 2 string to an int.'
>>> baseTwo('10010')
18
这个例子使用了 int()内建函数并将 base 固定为2来指定二进制字符串转化。现在没有多次用相同的第二参数2来调用int (), 比如('10010', 2),而可以只用带一个参数的新 baseTwo()函数。
注意警惕关键字:
这个例子如果你创建了不带 base 关键字的偏函数,比如, baseTwoBAD = partial(int, 2),这可能 会让参数以错误的顺序传入 int(),因为固定参数的总是放在运行时刻参数的左边, 比如 baseTwoBAD(x) == int(2, x)。如果你调用它,它会将2作为需要转化的数字,而base 作为'10010' 来传入,然后就产生一个异常:
>>> baseTwoBAD = partial(int, 2) #词句和上面的区别就在于没有写base=2
>>> baseTwoBAD('10010')
Traceback (most recent call last): File "<stdin>", line 1, in <module>
TypeError: an integer is required
五 变量作用域
1 全局变量和局部变量
2 globa语句
为了明确地引用一个已命名的全局变量,须使用global修饰:
global var1
3 闭包
若在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是 closure。定义在外部函数内的但由内部函数引用或者使用的变量被称为*变量。
简单闭包的例子:
def counter(start_at=0):
count = [start_at]
def incr():
count[0] += 1
return count[0]
return incr #注意返回的是一个可调用的函数对象!!!
>>> count = counter(5)
>>> print count()
6
>>> print count()
7
>>> count2 = counter(100)
>>> print count2()
101
>>> print count()
8
4 作用域和lambda
python的lambda匿名函数遵循和标准函数一样的作用域规则。一个 lambda 表达式定义了新的作用域。
六 生成器
生成器是一个带 yield语句的函数。一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果----那就是 yield 语句的功能,返回一个值给调用者并暂停执行。当生成器的 next()方法被调用的时候,它会准确地从离开地方继续
简单的生成器特性:
def simpleGen():
yield 1
yield '2 --> punch!'
>>> myG = simpleGen()
>>> myG.next()
1
>>> myG.next()
'2 --> punch!'
>>> myG.next()
Traceback ( most recent call last ):
File "", line 1, in ?
myG.next() StopIteration
from random import randint
def randGen(aList):
while len(aList) > 0:
yield aList.pop( randint(0, len(aList) ) )
>>> for item in randGen(['rock', 'paper', 'scissors']):
... print item
...
scissors
rock
paper
加强的生成器特性:
在 python2.5 中,一些加强特性加入到生成器中,所以除了next()来获得下个生成的值,用户可以将值回送给生成器,在生成器中抛出异常,以及要求生成器退出:
def counter(start_at=0):
count = start_at
while True:
val = (yield count)
if val is not None:
count = val
else:
count += 1
>>> count = counter(5)
>>> count.next()
5
>>> count.next()
6
>>> count.send(9)
9
>>> count.next()
10
>>> count.close()