python-函数和函数式编程
函数是对程序逻辑进行结构化或过程化的一种编程方法,能将整块代码隔离成易于管理的小块,把重复代码放到函数中而不是进行大量的拷贝。
1.1 返回值与函数类型
函数会向调用者返回一个值,在Python中,“什么都不返回”的函数对应的返回对象类型是None。另外,Python里的函数可以返回一个值或对象,只是在返回一个容器对象的时候有点不同,看起来像是能返回多个对象。
1 >>> def hello(): 2 ... print 'hello, world' 3 ... 4 >>> def foo(): 5 ... return ['xyz', 100, 12.1] 6 ...
1.2 调用函数
1.2.1 函数操作符
用一对圆括号调用函数(())。
1.2.2 关键字参数
关键字参数的概念针对的是函数的调用,让调用者通过函数调用中的参数名字来区分参数。例如:
1 def foo(x): 2 foo_suite() #presumably does some processing with ‘x’
标准调用foo():foo(42)、foo(‘bar’)
关键字调用foo():foo(x=42)、foo(x=’bar’)
1.2 创建函数
1.2.1 def语句
函数用def语句来创建:
1 def function_name(arguments): 2 ‘function_documentation_string’ 3 function_body_suite
标题行由def关键字,函数的名字,以及参数的集合(如果有)组成。def子句包括文档字符串(可选,但推荐写上)和必须的函数体。
1.2.2 声明和定义比较
Python将声明和定义视为一体。
1.2.3 前向引用
Python不允许在函数未声明之前,对其进行引用或调用。
1.2.4 内部/内嵌函数
Python可以在函数体内创建一个函数(对象),这种函数叫做内部/内嵌函数。
1 >>> def foo(): 2 ... print 'foo() called' 3 ... def bar(): 4 ... print 'bar() called' 5 ... bar() 6 ... 7 >>> foo() 8 foo() called 9 bar() called
内部函数的整个函数体都在外部函数的作用域之内。
1.2.6 函数(与方法)装饰器
装饰器背后的主要动机源自Python面向对象编程。装饰器是在函数调用之上的装饰。这些装饰仅是当声明一个函数或方法的时候,才会应用的额外调用。
装饰器的语法以@开头,接着是装饰器函数的名字和可选的参数。接着是装饰器函数的名字和可选的参数,紧跟着装饰器声明的是被装饰的函数和装饰函数的可选参数。
1 @decorator(dec_opt_args) 2 def func2Bdecorated(func_opt_args): 3 ;
1 #!/usr/bin/env python 2 3 from time import ctime, sleep 4 5 def tsfunc(func): 6 def wrappedFunc(): 7 print '[%s] %s() called' % (ctime(), func.__name__) 8 return func() 9 return wrappedFunc 10 11 @tsfunc 12 def foo(): 13 pass 14 15 foo() 16 sleep(4) 17 for i in range(2): 18 sleep(1) 19 foo() 20 V:\python>C:/Python27/python.exe v:/python/deco.py 21 [Thu Dec 12 17:01:21 2019] foo() called 22 [Thu Dec 12 17:01:26 2019] foo() called 23 [Thu Dec 12 17:01:27 2019] foo() called
1.3 传递函数
Python中函数是可以被引用的(访问或者以其他变量作为其别名),也可以作为参数传入函数,以及作为列表和字典等容器对象的元素。Python中所有的对象都是通过引用来传递的,函数也是,当对一个变量赋值时,实际是将相同对象的引用赋值给这个变量,如果对象是函数的话,这个对象所有别名都是可调用的。
1 >>> def foo(): 2 ... print 'in foo()' 3 ... 4 >>> bar = foo 5 >>> bar() 6 in foo()
1 #!/usr/bin/env python 2 3 def convert(func, seq): 4 'conv, sequence of numbers to some type' 5 return [func(eachNum) for eachNum in seq] 6 7 myseq = (123, 34.56, -6.3e8, 999L) 8 print convert(int, myseq) 9 print convert(float, myseq) 10 print convert(long, myseq) 11 V:\python>C:/Python27/python.exe v:/python/numConv.py 12 [123, 34, -630000000, 999] 13 [123.0, 34.56, -630000000.0, 999.0] 14 [123L, 34L, -630000000L, 999L]
1.4 Formal Arguments
1.4.1 位置参数
位置参数必须以在被调用函数中定义的准确顺序来传递。另外,没有任何默认参数的话,传入函数(调用)的参数的精确的数目必须和声明的数字一致。
1 def foo(who): #defined for only 1 argument 2 pass
1.4.2 默认参数
对于默认参数如果在函数调用时没有为参数提供值则使用预先定义的默认值。语法:参数名等号默认值。
1 def func(posargs, defarg1=dval1, defarg2=dval2, …): 2 function_body_suite
1.5 可变长度的参数
1.5.1 非关键字可变长参数(元组)
可变长的参数元组必须在位置参数和默认参数之后,带元组(或者非关键字可变长参数)的函数普遍的语法如下:
1 def function_name([formal_args,] *vargs_tuple): 2 ‘function_document_string’ 3 function_body_suite
星号操作符之后的形参将作为元组传递给函数,元组保存了所有传递给函数的“额外”的参数(匹配了所有位置和具体参数后剩下的),如果没有给出额外的参数,元组为空。
1 >>> def tupleVarArgs(arg1, arg2='defaultB', *theRest): 2 ... print 'formal arg1: ', arg1 3 ... print 'formal arg2: ', arg2 4 ... for eachXtrArg in theRest: 5 ... print 'another arg:', eachXtrArg 6 ... 7 >>> tupleVarArgs('abc', 123, 'xyz', 45.63) 8 formal arg1: abc 9 formal arg2: 123 10 another arg: xyz 11 another arg: 45.63
1.5.2 关键字变量参数(字典)
字典作为关键字参数的函数定义的语法为:
1 def function_name([formal_args, ] [*vargst, ] **vargsd): 2 ‘function_document_string’ 3 function_body_suite
为了区分关键字参数和非关键字非正式参数,使用了双星号(**)。**是被重载了的以便不予幂运算发生混淆。关键字变量参数应该为函数定义的最后一个参数,带**。
1 >>> def dictVarArgs(arg1, arg2='defaultB', **theRest): 2 ... 'display 2 regular args and keyword variable args' 3 ... print 'formal arg1: ', arg1 4 ... print 'formal arg2: ', arg2 5 ... for eachXtrArg in theRest.keys(): 6 ... print 'Xtra arg %s: %s' % (eachXtrArg, str(theRest[eachXtrArg])) 7 ... 8 >>> dictVarArgs('one', d=10, e='zoo', mem=('fred', 'guadi')) 9 formal arg1: one 10 formal arg2: defaultB 11 Xtra arg mem: ('fred', 'guadi') 12 Xtra arg e: zoo 13 Xtra arg d: 10
1.6 函数式编程
1.6.1 匿名函数与lambda
1 lambda [arg1, [, arg2, …, argN]]: Expression
Python允许使用lambda关键字创建匿名函数(匿名表示不需要按标准格式定义),一个完整的lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行。
1 def add(x, y): return x+y <==> lambda x, y: x+y 2 def usuallyAdd2(x, y=2): return x+y <==> lambda x, y=2: x+y 3 >>> lambda x, y=2: x+y 4 <function <lambda> at 0x7f0c902f5578> 5 >>> a = lambda x, y=2: x+y 6 >>> a(5) 7 7
1.6.2 内建函数filter()、map()、reduce()
函数式编程的内建函数
内建函数 |
描述 |
filter(func, seq) |
调用一个布尔函数func来迭代遍历每个seq中的元素;返回一个使func返回值为true的元素的序列 |
map(func, seq1[, seq2, …]) |
将函数func作用于给定序列(s)的每个元素,并用一个列表来提供返回值;如果func为None,func表现为一个身份函数,返回一个含有每个序列中元素集合的n个元组的列表。 |
reduce(func, seq[, init]) |
将二元函数作用于seq序列的元素,每次携带一对(先前的结果以及下一个序列元素),连续的将现有的结果和下一个值作用于在获得的随后的结果上,最后减少我们的序列为一个单一的返回值;如果初始值init给定,第一个比较会是init和第一个序列元素而不是序列的头两个元素。 |
filter():给定一个对象的序列和一个“过滤”函数,每个序列元素都通过这个过滤器进行筛选,保留函数返回为真的对象。filter函数为已知的序列的每个元素调用给定布尔函数,每个filter返回的非零(true)值元素添加到一个列表中。返回的对象是一个从原始队列中“过滤后”的队列。filter的用法大致如下:
1 def filter(bool_func, seq): 2 filtered_seq = [] 3 for item in seq: 4 if bool_func(item): 5 filtered_seq.append(item) 6 return filter_seq
举例:
1 >>> from random import randint 2 >>> def odd(n): 3 ... return n%2 4 ... 5 >>> allNums = [] 6 >>> for eachNum in range(9): 7 ... allNums.append(randint(1, 99)) 8 ... 9 >>> print filter(odd, allNums) 10 [85, 63, 99, 55, 17, 25]
map():map()函数与filter()类似,但将调用“映射”到每个序列的元素上,并返回一个含有所有返回值的列表。最简单的形式,map()带一个函数和队列,将函数作用在序列的每个元素上,然后创建由每个函数应用组成的返回值列表,解释代码如下:
1 def map(func, seq): 2 mapped_seq = [] 3 for item in seq: 4 mapped_seq.append(func(item)) 5 return mapped_seq
举例:
1 >>> map( (lambda x:x+2), [i**2 for i in range(6)]) 2 [2, 3, 6, 11, 18, 27]
多个序列作为其输入的情况:
1 >>> map(lambda x, y:x+y, [1, 2, 3], [4, 5, 6]) 2 [5, 7, 9] 3 >>> map(lambda x, y: (x+y, x-y), [1, 2, 3], [4, 5, 6]) 4 [(5, -3), (7, -3), (9, -3)] 5 >>> map(None, [1, 2, 3], [4, 5, 6]) 6 [(1, 4), (2, 5), (3, 6)]
reduce():reduce使用一个二元函数(一个接收带两个值作为参数,进行了一些计算然后返回一个值作为输出),一个序列,和一个可选的初始化器,将列表的内容“减少”为一个单一的值,这个概念也称为折叠。它通过取出序列的头两个元素,将他们传入二元函数来获得一个单一的值来实现,然后又用这个值和序列的下一个元素来获取得又一个值,然后继续直到整个序列的内容都遍历完毕以及最后的值被计算出来为止。解释代码如下:
1 def reduce(bin_func, seq, init = None): 2 lseq = list(seq) 3 if init is None: 4 res = lseq.pop(0) 5 else: 6 res = init 7 for item in lseq: 8 res = bin_func(res, item) 9 return res
举例:
1 >>> def mysum(x, y): return x+y 2 ... 3 >>> allnums = range(5) 4 >>> total = 0 5 >>> for eachNum in allnums: 6 ... total += eachNum 7 ... 8 >>> print total 9 10 10 >>> print 'the total is ', reduce((lambda x,y:x+y), range(5)) 11 the total is 10
1.7 变量作用域
标识符的作用域是定义其声明在程序里的可应用范围,变量可以是全局域或局部域。
1.7.1 全局变量与局部变量
声明适用的程序的范围被称为了声明的作用域,在一个过程中,如果名字在过程的声明之内,它的出现即为过程的局部变量,否则的话,出现即为非局部。
1 >>> global_str = 'foo' 2 >>> def foo(): 3 ... local_str = 'bar' 4 ... return global_str + local_str 5 ...
当搜索一个标识符的时候,Python先从局部作用域开始搜索,如果在局部作用域内没有找到那个名字,那么一定会在全局域找到这个变量否则就会抛出NameError异常。
1.7.2 global语句
为了明确的引用一个已命名的全局遍历,必须使用global语句。
1 >>> is_this_global = 'xyz' 2 >>> def foo(): 3 ... global is_this_global 4 ... this_is_local = 'abc' 5 ... is_this_global = 'def' 6 ... print this_is_local + is_this_global 7 ... 8 >>> foo() 9 abcdef 10 >>> is_this_global 11 'def'
1.8 递归
如果函数包含了对自身的调用,该函数就是递归的。
例如求N的阶乘
1 >>> def factorial(n): 2 ... if n == 0 or n == 1: 3 ... return 1 4 ... else: 5 ... return factorial(n-1)*n 6 ... 7 >>> factorial(5) 8 120
1.9 生成器
从语法上讲,生成器是一个带yield语句的函数,一个函数或者子程序只返回一次,但一个生成器能够暂停执行并返回一个中间的结果(yield语句的功能),返回一个值给调用者并暂停执行,当生成器的next()方法被调用的时候,它会准确的从离开地方继续。
1.9.1 简单的生成器特性
当到达一个真正的返回或者函数结束没有更多的值返回(当调用next()),一个StopIteration异常就会抛出。也可以使用for循环手动迭代穿过一个生成器。
1 >>> def simpleGen(): 2 ... yield 1 3 ... yield '2 --> punch!' 4 ... 5 >>> myG = simpleGen() 6 >>> myG.next() 7 1 8 >>> myG.next() 9 '2 --> punch!' 10 >>> myG.next() 11 Traceback (most recent call last): 12 File "<stdin>", line 1, in <module> 13 StopIteration