转载

Python基础教程

6.4.5 参数收集的逆过程

假设有如下函数:

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

星号只在 定义函数 (允许使用不定数目的参数)或者 调用 (“分割”字典或者序列)时才有用。

6.5 作用域

在执行 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)。

6.6 递归

递归的定义(包括递归函数定义)包括它们自身定义内容的引用。

关于递归,一个类似的函数定义如下:

def recursion():     return recursion()

理论上讲,上述程序应该永远地运行下去,然而每次调用函数都会用掉一点内存,在足够的函数调用发生后(在之前的调用返回后),空间就不够了,程序会以一个“超过最大递归深度”的错误信息结束。

这类递归就做 无穷递归 (infinite recursion),类似于以 while True 开始的无穷循环,中间没有 break 或者 return 语句。因为(理论上讲)它永远不会结束。

有用的递归函数包含以下几个部分:

  1. 当函数直接返回值时有基本实例(最小可能性问题)

  2. 递归实例,包括一个或者多个问题较小部分的递归调用。

这里的关键就是将问题分解成小部分,递归不可能永远继续下去,因为它总是以最小可能性问题结束,而这些问题又存储在基本实例中的。

当每次函数被调用时,针对这个调用的新命名空间会被创建,意味着当函数调用“自身”时,实际上运行的是两个不同的函数(或者说是同一个函数具有两个不同的命名空间)。实际上,可以将它想象成和同种类的一个生物进行对话的另一个生物对话。

6.6.1 递归经典案例:阶乘和幂

计算数 n 的的阶乘:

def factorial(n):     result = n     for i in range(1,n):         result *= 1         return result

递归实现:

  1. 1的阶乘是1;

  2. 大于1的数n的阶乘是 nn-1 的阶乘。

def factorial(n):     if n == 1:         return 1     else:         return n * factorial(n-1)

======

计算幂

例子: power(x,n) ( xn 的幂次)是 x 自乘 n-1 次的结果(所以 x 用作乘数 n 次。

def power(x,n):     result = 1     for i in range(n):         result *= x     return result

递归实现:

  1. 对于任意数字 x 来说,`power(x,0)是1;

  2. 对于任何大于0的书来说, power(x,n)x 乘以 (x,n-1) 的结果。

def power(x,n):     if n == 0:         return 1     else:         return x * power(x,n-1)

6.6.2 递归经典案例:二分法查找

递归实现:

  1. 如果上下限相同,那么就是数字所在位置,返回;

  2. 否则找到两者的中点(上下限的平均值),查找数字是在左侧还是在右侧,继续查找数字所在的那半部分。

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在应对“函数式编程”方面有一些有用的函数: mapfilterreduce 函数(Python3.0中都被移至 fuctools 模块中)。

mapfilter 在目前版本的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

6.7 小结

  • 抽象。抽象是隐藏多余细节的艺术。定义处理细节的函数可以让程序更抽象。

  • 函数定义。函数使用 def 语句定义。它们是由语句组成的块,可以从“外部世界”获取值(参数),也可以返回一个或者多个值作为运算的结果。

  • 参数。函数从参数中得到需要的信息,也就是函数调用时设定的变量。Python中有两类参数: 位置参数关键数参数 。参数在给定默认值时是可选的。

  • 作用域。变量存储在作用域(也叫作命名空间)中。Python有两类主要的作用域—— 全局作用域局部作用域 。作用域可以嵌套。

  • 递归。 函数可以调用自身即递归。一切用递归实现的功能都能用循环实现,但是有些时候递归函数更易读。

  • 函数式编程。Python有一些进行函数式编程的机制。包括 lambda 表达式以及 mapfilterreduce 函数。

6.7.1 本章的新函数

| 函数 | 描述 |

| :--------- |:------|

| 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]] | 调用函数,可以提供参数 |

原文  https://segmentfault.com/a/1190000004999556
正文到此结束
Loading...