假设有如下函数:
def add(x,y): return x+y
比如说有个包含由两个相加的数字组成的 元组 :
params = (1,2)
使用 *
运算符对参数进行“分配”,不过是在调用而不是在定义时使用:
>>> add(*params) 3
======
同样,可以使用 双星号 运算符来处理字典。
假设之前定义了 hello_3
,那么可以这样使用:
>>> params = {'name':Sir Robin','greeting':'Well met'} >>> hello_3(**params) Well met.Sir Robin
星号只在 定义函数 (允许使用不定数目的参数)或者 调用 (“分割”字典或者序列)时才有用。
在执行 x=1
赋值语句后,名称 x
引用到值 1
。这就像是使用字典一样, 键引用值 。当然,变量和所对应的值用的是个“不可见”的字典。
內建的 vars
函数可以返回这个字典:
>>> x = 1 >>> scope = vars() >>> scope['x'] 1 >>> scope['x'] += 1 >>> x 2
这类“不可见字典”叫做 命名空间 或者 作用域 。除了全局作用域外,每个函数调用都会创建一个新的作用域:
>>> def foo(): x = 42 ... >>> x = 1 >>> foo() >>> x 1
这里的foo函数改变(重绑定)了变量x,但是在最后的时候,x并没有变。这是因为当调用 foo
的时候,新的命名空间就被创建了,它作用于 foo
内的代码块。赋值语句 x=42
只在内部作用域(局部命名空间)起作用,所以它并不影响外部(全局)作用域中的 x
。
函数内的变量被称为 局部变量 (local variable),这是与全局变量相反的概念。参数的工作原理类似于局部变量,所以用全局变量的名字作为参数名并没有问题。
>>> def output(x): print x ... >>> x = 1 >>> y = 2 >>> output(y) 2
======
重绑定全局变量:
如果在函数内部将值赋予一个变量,它将会自动成为局部变量——除非告知Python将其声明为全局变量:
>>> x = 1 >>> def change_global(): global x x = x + 1 >>> change_global() >>> x 2
======
Python的函数是可以嵌套的:
def foo(): def bar(): print "Hello,World!" bar()
函数嵌套有一个很突出的应用,例如需要一个函数“ 创建 ”另一个。也就意味着可以像下面这样(在其他函数内)书写函数:
def multiplier(factor): def multiplier(number): return number*factor returnmultiplyByFactor
一个函数位于另外一个里面,外层函数返回里层函数。也就是说函数本身被返回了,但并没有被调用。重要的是返回的函数还可以 访问它的定义所在的作用域 。换句话说,它“带着”它的环境(和相关的局部变量)。
每次调用外层函数,它内部的函数都被重新绑定。 factor
变量每次都有一个新的值。由于Python的嵌套作用域,来自( `multiplier
的)外部作用域的这个变量,稍后会被内层函数访问:
>>> double = multiplier(2) >>> double(5) 10 >>> triple = multiplier(3) >>> triple(3) 9 >>> multiplier(5)(4) 20
类似 multiplayByFactor
函数 存储子封闭作用域 的行为叫做闭包(closure)。
递归的定义(包括递归函数定义)包括它们自身定义内容的引用。
关于递归,一个类似的函数定义如下:
def recursion(): return recursion()
理论上讲,上述程序应该永远地运行下去,然而每次调用函数都会用掉一点内存,在足够的函数调用发生后(在之前的调用返回后),空间就不够了,程序会以一个“超过最大递归深度”的错误信息结束。
这类递归就做 无穷递归 (infinite recursion),类似于以 while True
开始的无穷循环,中间没有 break
或者 return
语句。因为(理论上讲)它永远不会结束。
有用的递归函数包含以下几个部分:
当函数直接返回值时有基本实例(最小可能性问题)
递归实例,包括一个或者多个问题较小部分的递归调用。
这里的关键就是将问题分解成小部分,递归不可能永远继续下去,因为它总是以最小可能性问题结束,而这些问题又存储在基本实例中的。
当每次函数被调用时,针对这个调用的新命名空间会被创建,意味着当函数调用“自身”时,实际上运行的是两个不同的函数(或者说是同一个函数具有两个不同的命名空间)。实际上,可以将它想象成和同种类的一个生物进行对话的另一个生物对话。
计算数 n
的的阶乘:
def factorial(n): result = n for i in range(1,n): result *= 1 return result
递归实现:
1的阶乘是1;
大于1的数n的阶乘是 n
乘 n-1
的阶乘。
def factorial(n): if n == 1: return 1 else: return n * factorial(n-1)
======
计算幂
例子: power(x,n)
( x
为 n
的幂次)是 x
自乘 n-1
次的结果(所以 x
用作乘数 n
次。
def power(x,n): result = 1 for i in range(n): result *= x return result
递归实现:
对于任意数字 x
来说,`power(x,0)是1;
对于任何大于0的书来说, power(x,n)
是 x
乘以 (x,n-1)
的结果。
def power(x,n): if n == 0: return 1 else: return x * power(x,n-1)
递归实现:
如果上下限相同,那么就是数字所在位置,返回;
否则找到两者的中点(上下限的平均值),查找数字是在左侧还是在右侧,继续查找数字所在的那半部分。
def search(sequence,number,lower,upper): if lower == upper: assert number == sequence[upper] return upper else: #整数除法//,浮点数除法/ middle = (lower + upper) // 2 if number > sequence[middle]: return search(sequence,number,middle+1,upper) else: return search(sequence,number,lower,middle)
提示:标准库中的 bisect
模块可以非常有效地实现二分查找。
Python在应对“函数式编程”方面有一些有用的函数: map
、 filter
和 reduce
函数(Python3.0中都被移至 fuctools
模块中)。
map
和 filter
在目前版本的Python并非特别有用,并且可以使用 列表推导式 代替。不过可以使用 map
函数将序列中的元素全部传递给一个函数:
>>> map(str,range(10))` #Equivalent to [str(i) for i in range(10)] ['0','1','2','3','4','5','6','7','8','9']
filter
函数可以基于一个返回布尔值的函数对元素进行过滤。
#island 判断字符变量是否为字母或数字, #若是则返回非零,否则返回零 >>> def fun(x): return x.isalnum() >>> seq = ["foo","x41","?!","***"] >>> filter(func,seq) ['foo','x41']
本例中,使用列表推导式可以不用专门定义一个函数:
>>> [x for x in seq if x.isalnum()] ['foo','x41']
事实上,还有个叫做 lambda
表达式的特性,可以创建短小的函数。
>>> filter(lambda x: x.isalnum().seq) ['foo','x41']
=======
reduce
函数一般来说不能轻松被列表推导式替代,但是通常用不到这个功能。它会 将序列的前两个元素与给定的函数联合使用,并且将它们的返回值和第3个元素继续联合使用,直到整个序列都处理完毕,并且得到一个最终结果 。
可以使用 reduce
函数加上 lambda x,y:x+y
(继续使用相同的数字):
>>> numbers = [72,101,108,108,111,44,32,119,111,114,108,100,33] >>> reduce(lambda x,y:x+y,numbers) 1161
当然,这里也可以使用内建函数 sum
。
抽象。抽象是隐藏多余细节的艺术。定义处理细节的函数可以让程序更抽象。
函数定义。函数使用 def
语句定义。它们是由语句组成的块,可以从“外部世界”获取值(参数),也可以返回一个或者多个值作为运算的结果。
参数。函数从参数中得到需要的信息,也就是函数调用时设定的变量。Python中有两类参数: 位置参数 和 关键数参数 。参数在给定默认值时是可选的。
作用域。变量存储在作用域(也叫作命名空间)中。Python有两类主要的作用域—— 全局作用域 和 局部作用域 。作用域可以嵌套。
递归。 函数可以调用自身即递归。一切用递归实现的功能都能用循环实现,但是有些时候递归函数更易读。
函数式编程。Python有一些进行函数式编程的机制。包括 lambda
表达式以及 map
、 filter
和 reduce
函数。
| 函数 | 描述 |
| :--------- |:------|
| map(func,seq[,seq,...])
| 对序列中的每个元素应用函数 |
| filter(fuc,seq)
| 返回其函数为真的元素的列表 |
| reduce(func,seq[,initial])
| 等同于 func(func(func(seq[0],seq[1],se1[2]...
|
| sum(seq)
| 返回 seq
所有元素的和 |
| apply(func,args[,kwargs]]
| 调用函数,可以提供参数 |