Python 抽象

来源:互联网 发布:淘宝产品图片尺寸留白 编辑:程序博客网 时间:2024/06/05 10:56

Python抽象

6.1 懒惰即美德

目前为止我们所写的程序都很小,如果想编写大型的程序,很快就会遇到麻烦。考虑一下如何在一个地方边写一段代码,但是在另一个地方也要用到这段代码,这是会发生什么。例如,假设我们编写了一下段代码来计算斐波那契数列(任一数都是前两数之和的数字序列):
fibs = [0,1]n = raw_input()for i in range(int(n)):    fibs.append(fibs[-2]+fibs[-1])print fibs
输出:
8
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

但是如果想用这些数字做其他事呢?当然可以在需要的时候重写同样的循环,但是如果已经编写的是一段复杂的代码——比如下载一系列网页并计算词频——应该怎么做呢?
你是否希望在每次需要的时候把所有的代码重写一遍呢?当然不用,真正的程序员不会这样做的,他们都很懒,但不是用错误的方式懒惰,换句话说他们不做无用功。

6.2 抽象和结构

抽象可以节省很多工作,实际上它的作用还要更大,他是使得计算机程序可以让人读懂的关键。计算机非常乐于处理精确和具体的指令,但是人就不同了。人不需要明确指令来指导一些简单的事情。组织计算机程序也是类似的。程序应该是非常抽象的,就像“下载网页、计算频率、打印每个单词的频率”一样易懂。

6.3 创建函数

函数是可以掉用的,他执行某种行为并且返回一个值。一般来说,内建的callable函数可以用来判断函数是否可调用:
import mathx = 1y = math.sqrtprint callable(x)print callable(y)
输出:
False
True
就像前一节内容中介绍的,创建函数是组织程序的关键。那么怎么定义函数呢?使用def(或“函数定义”)语句即可:
def hello(name):    return 'Hello.'+name+'!'print hello('World')print hello('Gumby')
输出:
Hello.World!
Hello.Gumby!
def fibs(num):    result = [0,1]    for i in range(num-2):        result.append(result[-2]+result[-1])    return resultprint fibs(10)print fibs(15)
输出:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]

6.3.1 文档化函数

如果想要给函数2写文档,让其他使用该函数人能理解的话,可以加注释。另外一个方式就是直接写上字符串。这类字符串在其他地方可能会非常有用,比如在def语句后面。
如果在函数的开头写下字符串,他就会作为函数的一部分进行储存,这称为文档字符串。
<pre name="code" class="python">def square(x):    'Calculates the square of the number x.'    return x*xprint square.__doc__print help(square)
输出:
Calculates the square of the number x.Help on function square in module __main__:square(x)    Calculates the square of the number x.None
__doc__是函数属性。
内建的help函数是非常有用的。在交互式解释器中使用它,就可以得到关于函数,包括它的文档字符串信息。

6.3.2 并非真正函数的函数

数学意义上的函数,总在计算其参数后返回点什么。Python的有些函数却并不返回任何东西。在其他语言中(比如Pascal),这类函数可能有其他名字(比如过程)。但是Python的函数就是函数,即便他从学术上讲不是函数。没有return语句,或者虽有return语句但return后边没有跟任何值的函数不返回值:
def test():    print 'This is printed'    return    print 'This is not'x = test()print x
输出:
This is printed
None

6.4 参数魔法

函数使用起来很简单,创建起来也不复杂。但函数参数的用法有时就有些神奇了。还是从最基础的介绍起。

6.4.1 值从哪里来

函数被定义后,做操作的值是从哪里来的呢?一般来说不用担心这些,编写函数只会给程序需要的部分提供服务,能保证函数在被提供给可接受参数的时候正常工作就行,参数错误的话显然会导致失败。
注意:写在def语句中函数名后面的变量通常叫做函数的形参,而调用函数的时候提供的值是实参,或者称为参数。

6.4.2 我能改变参数吗

函数通过他的参数获得一系列值。那么这些值能改变吗?如果改变了又会怎样?参数只是变量而已,所以他们的行为其实和你预想的一样。在函数内为参数赋予新值不会改变外部变量的值:
def try_to_change(n):    n =  'Mr.Gumby'name = 'Mrs . Entity'try_to_change(name)print namen = namen = 'Mr . Gumby'print name
输出:
Mrs . Entity
Mrs . Entity
结果是显而易见的。当变量n改变的时候,变量name不变。同样,当在函数的内部把参数重绑定得时候,函数外的变量不会受到影响。

字符串是不可变的,即无法被修改。所以他们做参数的时候也就无需多做介绍。但是考虑一下如果将可变的数据结构如列表用作参数的时候会发生什么:
def change(n):    n[0] = 'Mr.Gumby'names = ['Mrs.Entity','Mrs.Thing']change(names)print names
输出:
['Mr.Gumby', 'Mrs.Thing']
本例中,参数被改变了。这就是本例和前面例子中至关重要的区别。前面例子中,局部变量被赋予了新值,但是这个例子中变量names所绑定的列表的确变了。

6.4.3 关键字参数和默认值

目前为止我们所使用的参数都叫做位置参数,因为他们的位置很重要,事实上比他们的名字更加重要。本节中引入的这个功能可以回避位置问题,当你慢慢习惯使用这个功能以后,就会发现程序规模越大,他们的作用也就越大。
考虑下面的两个函数:
def hello_1(greeting,name):    return '%s,%s!'%(greeting,name)def hello_2(name,greeting):    return '%s,%s!'%(name,greeting)print hello_1('hello','world')print hello_2('hello','world')print hello_1(greeting='hello',name='world')print hello_2(name='hello',greeting='world')
输出:
hello,world!
hello,world!
hello,world!
hello,world!

6.4.4 收集参数

有些时候让用户提供任意数量的参数是很有用的。比如在名字储存程序中,用户每次只能存一个名字。
def print_params(*params):    return paramsprint print_params('Testing')print print_params(1,2,3)def print_params_2(title,*params):    print title    return paramsprint print_params_2('Params:',1,2,3,4,5)print print_params_2('Nothing:')def print_params_3(**params):    return paramsprint print_params_3(x=1,y=2,z=3)def print_params_4(x,y,z=3,*pospar,**keypar):    print x,y,z    print pospar    return keyparprint print_params_4(1,2,3,5,6,7,f00=1,bar=2)
输出:
('Testing',)
(1, 2, 3)
Params:
(1, 2, 3, 4, 5)
Nothing:
()
{'y': 2, 'x': 1, 'z': 3}
1 2 3
(5, 6, 7)
{'bar': 2, 'f00': 1}

6.4.5 参数收集的逆过程

如何将参数收集为元祖和字典已经讨论过了,但事实上,如果使用*和**的话,也可以执行相反的操作。
def add(x,y):    return x+yparams = (1,2)print add(*params)
输出:
3
def hello_3(greeting,name):    return '%s,%s!'%(greeting,name)params = {'name':'Sir Robin','greeting':'Well met'}print hello_3(**params)
输出:
Well met,Sir Robin!

6.4.6 练习使用参数

def story(**kwds):    return 'Once upon a time.there was a '\           '%(job)s called %(name)s.'%kwdsdef power(x,y,*others):    if others:        print 'Received redundant parameters:',others    return pow(x,y)def interval(start,stop=None,step=1):    'Imitates range() for step>0'    if stop is None:        start,stop = 0,start    result = []    i = start    while i<stop:        result.append(i)        i+=step    return resultprint story(job='king',name='Gumby')print story(name='Sir Robin',job='brave knight')params = {'job':'language','name':'Python'}print story(**params)del params['job']print story(job='stroke of genius',**params)print power(2,3)print power(3,2)params = (5,)*2print power(*params)print power(3,3,'Hello World!')print interval(10)print interval(1,5)print interval(3,12,4)print interval(3,7)print power(*interval(3,7))
输出:
Once upon a time.there was a king called Gumby.
Once upon a time.there was a brave knight called Sir Robin.
Once upon a time.there was a language called Python.
Once upon a time.there was a stroke of genius called Python.
8
9
3125
Received redundant parameters: ('Hello World!',)
27
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4]
[3, 7, 11]
[3, 4, 5, 6]
Received redundant parameters: (5, 6)
81

6.5 作用域

到底什么是变量?你可以把他们看作是值的名字。在执行x = 1赋值语句后,名称x引用到值1.这就像用字典一样,键引用值,当然,变量和所对应的值用的是个不可见的字典。实际上这么说已经很接近真实情况了。内建的vars函数可以返回这个字典。
x=1scope = vars()print scope['x']
输出:
1
这类“不可见的字典”叫做命名空间或者作用域。那么到底有多少个命名空间?除了全局作用域外,每个函数调用都会创建一个新的作用域:
def foo():    x = 42    returnx =1foo()print x
输出:
1
这里的foo函数改变了变量x,但是在最后的时候,x并没有变。这是因为当调用foo的时候,新的命名空间就被创建了,它作用于foo内的代码块。赋值语句下x = 42只在内部作用域起作用,所以他并不影响外部作用域中的x。函数内的变量被称为局部变量。参数的工作原理类似于局部变量,所以用全局变量的名字作为参数名并没有问题。
def output(x):    return xx=1y=2print output(y)
输出:
2

6.6 递归

# _*_ coding:utf8 _*_#递归求n的阶乘:def factorial(n):    if n == 1:        return 1    else:        return n*factorial(n-1)n = input()print factorial(n)
输出:
4
24
def search(array,n):    low = 0    height = len(array)    while low < height:        mid = (low + height) / 2        if array[mid] < n:            low = mid+1        elif array[mid] > n:            height = mid-1        elif array[mid] == n:            return True    return Falsearray = [1,2,3,4,5,6,7,8,9,10]print search(array,6)print search(array,11)
输出:
True
False

























1 0
原创粉丝点击