1.前言
接着上一节的内容,我们在这节继续说关于抽象的内容。这节涉及的内容是关于函数的一些内容:主要是参数的作用域、递归。然后会介绍一些我们在C语言里面经常见得一些算法。内容不是很多,但是很重要,要认真。
2.作用域
到目前为止,我们已经学了不少的知识,到底什么是变量?举个例子:x=1,当我们在解释器里面写入这样的代码的时候,名称x引用到值1。这就像字典一样,键引用值。但是在这里,x=1这样的键-值引用对我们来说是一种“不可见”的字典。而这类“不可见”的字典我们就称之为命名空间或者作用域。那么到底有多少个命名空间?一般地,除了全局作用域以外,每个函数调用都会创建一个新的作用域。我们来看看一些例子:
>>> def test(): x=100 ... >>> x=0 >>> test() >>> x 0
我们写了一个test函数,在函数体外我们先给x变量绑定一个值,然后调用test函数对x变量重新绑定。但是,在最后的打印结果显示,x的值并没有发生变化。这是因为当调用test函数的时候,新的命名空间就被创建了,这个命名空间只作用于函数体内的代码块,也就是说它只在局部命名空间起作用,从而它并不影响全局作用域的x。参数的工作原理类似于局部变量,所以用全局变量的名字作为参数名并没有什么问题。
到目前为止,暂时还没有出现什么问题。有时候你想在函数体内访问全局变量也没有问题,你只想读取它而不是去重新绑定它,这是可行的。但是如果你想去重新绑定全局变量?应该怎么办?
如果你在函数体内创建一个变量,它会自动成为一个局部变量,除非是你在函数体内声明这是一个全局变量。使用global关键字来进行声明。
#coding:utf-8 #author:youzi x=1 def change_global(): global x x+=1 change_global() print x
/usr/local/bin/python2.7 /Users/yangjiayuan/PycharmProjects/day/day11/change_of_global.py 2 Process finished with exit code 0看看上面这个例子,在声明了global全局变量以后我们看到x的值被改变了,要是不是必须得,这样的操作一般不要执行,许多问题都是由此产生的。慎重使用全局变量!
3.递归
递归这个工具终于要登场了,简答地来讲递归就是函数自己调用自己,自己调用自己本身。一个类似的递归定义如下:
>>> def recursion(): return recursion()
显然我们可以看到,这个函数做不了任何事情。但是从理论上讲,这个函数应该永远执行下去,每运行一次就会消耗内存,最后会以失败告终并会抛出错误:maximum recursion depth exceeded,超过最大递归深度。
这类递归我们称为无穷递归,类似于循环的条件一直为真的情况。我们要的是一些正常而且有用的递归方式,通常有用的递归函数有下面几个部分:
- 当函数直接返回值时候有基本实例。
- 递归实例,包括一个或者多个问题较小部分的递归调用。
这里问题的关键就是将问题分解为小部分,递归不可能永远执行下去,因为它总是以最小可能性问题结束,而这些问题又存储在基本实例中。每次函数被调用的时候,针对这个调用的新命名空间就会被创建,意味着当函数调用“自身”时,实际上运行的是两个不同的函数,或者是说同一个函数具有两个不同的命名空间。
下面来看两个实例:阶乘和二分法
- 阶乘
def factorial(n): result=n for i in range(1,n): result*=i print result factorial(10)
def factorial(n): if n==1: return 1 else: return n*factorial(n-1) print factorial(10)
- 二分法
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)好了,抽象这部分内容就先到这里了,下一节我会介绍更加抽象的内容--面向对象编程。敬请期待!