美文网首页python & flaskpython学习
《Python基础教程》第6章 抽象

《Python基础教程》第6章 抽象

作者: tangyefei | 来源:发表于2015-01-12 19:38 被阅读164次

    第6章 抽象

    本章会介绍如何将代码组织成函数,并会详细介绍函数的参数、作用域,以及递归在程序中的用途。

    懒惰即美德(略)

    抽象和结构(略)

    创建函数

    如下是一个简单的定义函数的例子:

    def hello(name):
        print "Hello,", name, "!"
    
    记录函数

    如果在函数的开头写下字符串,字符串会被当作函数的一部分进行存储:

    def  square(x):
        'Calculate the square of x'
        return x*x
    

    文档字符串可以按照如下方式访问:

    square.__doc__
    

    内建函数help在命令行中非常有用,通过如下方式就可以得到关于函数的具体信息:

    help(square)
    
    无返回值的函数

    虽然没有现实return内容,但是所有函数都返回了东西,当不需要它们返回值的时候它们返回None。

    参数详解

    值从哪里来

    定义函数时候的参数叫做形式参数,调用函数时的参数叫做实际参数。

    函数封装的例子
    def try_to_change(n):
        n = 'Hello, Entity!'
    name  = 'Hello, Gumby!'
    try_to_change(name)
    print name
    
    $ python hello.py
    Hello, Gumby!
    
    
    

    看如上例子,我们发现,调用try_to_change后,值在内部发生了改变,但是并没有影响到外部的name。那么再看下面这个例子:

    def change(n):
        n[0] = 'Mr.Gumby'
    names = ['Mr.Entity', 'Mrs.Lucy']
    change(names)
    print names
    
    $ python hello.py
    ['Mr.Gumby', 'Mrs.Lucy']
    

    这里我们可以看到,列表在函数内的改变对外面的列表产生了影响,和前一个例子的区别就在于:第一个例子中,字符串是不可变的,函数内部进行赋值表示指向了一个新的字符串,而第二个例子则并没有改变之前的列表对象,只是改变之前列表的其中一个元素。如果要改变列表你可以直接将列表指向新的对象或者干脆指向None试试:

    def change(n):
        n = None
    names = ['Mr.Entity', 'Mrs.Lucy']
    change(names)
    print names
    
    $ python hello.py
    ['Mr.Entity', 'Mrs.Lucy']
    
    

    如下用例子告诉你:将代码抽象函数的好处(个人觉得例子还不错,费些周章看看最好手动实现一下还是值得的)。

    现在嘉定要写一个根据firstname、middlename、lastname来查找联系人完整名字的程序。复习下前面学习到的知识,用最原始的方法构建一个程序:

    contact_book = {}
    contact_book["first"] = {}
    contact_book["middle"] = {}
    contact_book["last"] = {}
    me = "Magnus Lie Hetland"
    
    contact_book["first"]["Magnus"] = me
    contact_book["middle"]["Lie"] = me
    contact_book["last"]["Hetland"] = me
    
    print contact_book['middle']['Lie']
    
    $ python hello.py
    Magnus Lie Hetland
    
    

    如上代码可以实现功能,但是当需要添加多个用户到contact_book的时候,代码的冗长可想而知。

    我们可以对所涉及的步骤进行简单的抽象:

    contact_book = {} #定义姓名字典
    init(contact_book) // #初始化结构
    store(contact_book, "Magnus Lie Hetland") #存储单姓名记录
    lookup(contact_book, 'middle', 'Lie') //查询符合条件的记录
    
    

    如下是对关键代码的实现:

    def init(book):
        book['first'] = {}
        book['middle'] = {}
        book['last'] = {}
    
    def lookup(book, label, value):
        return book[label].get(value)
    
    def store(book, full_name):
        names = full_name.split(' ')
        if len(names) == 2: names.append(1, ' ')
        labels = ['first', 'middle', 'last']
        for name, label in zip(names, labels):
            people = lookup(book, label, name)
            if people:
                people.append(full_name)
            else:
                book[label][name] = [full_name]
    
    contact_book = {}
    init(contact_book)
    store(contact_book, "Magnus Lie Hetland")
    store(contact_book, "Magnus Baz Hetland")
    print lookup(contact_book, 'middle', 'Lie')
    print lookup(contact_book, 'first', 'Magnus')
    
    
    $ python hello.py
    ['Magnus Lie Hetland']
    ['Magnus Lie Hetland', 'Magnus Baz Hetland']
    
    

    上述代码实现过程中可能有两点需要注意的,一个是book[label].get(value)直接写作book[label][value]是会报错的,因为可能键不存在;第二个是zip的运用,在构思程序的时候,需要比较名字的各个部分在book['first']、book['middle']、book['last']是否存在,自己的代码实现可能会很丑陋,使用zip使这一处理变得简单了。

    原书在该部分讨论的一个重点是,为什么要改变参数,如上例我们大概已经知道了,某些数据结构就是用来存储和被改变的;另外一些时候,传递进来的参数是不能改变的,这时候我们可以构建满足需求的结果返回出去即可。

    关键字参数和默认值

    前面一小节的例子中我们使用参数都是和位置关联的,调用的时候不能错乱了位置。Python还提供一种称作关键字参数的用法,除了不用记忆参数顺序外,还可以设置默认值:

    def hello(greeting="Hello", name="World"):
        print greeting + ',', name
    
    hello()
    hello(name='MingZe')
    hello(name="MingZe", greeting="Nice to meet you")
    
    $ python hello.py
    Hello, World
    Hello, MingZe
    Nice to meet you, MingZe    
    
    
    收集参数

    可以让用户提供任意数量的参数(所谓联合参数)也是Python函数的一个功能,看如下示例:

    def print_params(*params):
        print params
    
    print_params()
    print_params("Hello")
    print_params(1, 2, 3)
    
    $ python hello.py
    ()
    ('Hello',)
    (1, 2, 3)
    
    

    还可以将普通的参数和联合参数混合使用:

    def print_params(title, *params):
        print title, params
    
    print_params("Params:", 1, 2, 3)
    
    $ python hello.py
    Params: (1, 2, 3)
    
    

    要将关键字参数(key=value的形式)也收录到params中,需要使用**params,注意返回的结果是字典:

    $ python hello.py
    {'y': 2, 'x': 1, 'z': 3}
    

    将几种参数方式混合起来使用:

    def print_params(x, y=3, z=4, *pospar, **keypar):
        print x, y, z
        print pospar
        print keypar
    
    print_params(1, 2, 3, 4, 5, 6, 7, foo=1, bar = 2)
    
    $ python hello.py
    1 2 3
    (4, 5, 6, 7)
    {'foo': 1, 'bar': 2}
    

    学习过了联合参数的使用以后,再回到上面想过的存储个人姓名的程序,就可以通过 *full_names 来一次存储多个姓名了。

    反转过程

    在上节中我们在函数定义中使用了 *params,然后实际调用的时候传递多个参数,比如1,2,3,参数会被收集到params中; **params和x=1, y=2同理。

    反过来看,如果函数定义的是如下形式 def func(x,y),那么调用的时候就可以通过 params=(x, y) func(params), 同样对于关键字参数也是如此,定义def func(params), 调用params={x:1,y:2} func(*params),如下例所示:

    def foo(x, y, z, m=0, n=0):
        print x, y, z, m, n
    
    args = (1,2,3)
    kwds = {"m":4, "n": 5}
    foo(*args, **kwds)
    
    
    $ python hello.py
    1 2 3 4 5
    
    练习使用参数

    该书使用了一个综合性稍强的练习题,任务中自己多体味吧。

    作用域

    我们通过执行形如x=1的赋值语句后,就能通过过x直接访问到值是因为python中做了形如 scope['x'] = 1的操作,这里的scope就是作用域。

    作用于分为全局作用域和局部作用域,每个执行函数内部都创建一个局部作用域,看如下例子:

    def foo(n):
        n = 42
    num = 1
    foo(num)
    print num
    
    $ python hello.py
    1
    
    

    在函数中我们修改了参数的值,但在函数外面进行访问却仍旧是原来的值,这是因为n作为数字类型,会在函数内被新创建为局部作用域内的值,跟外部传入的不是同一个。

    如果要对值进行操作,只要将修改后的值作为函数的返回值返回即可。

    在函数内想要访问全局作用域内的值也很简单:

    external = 'Brozu'
    def foo(param):
        return param + external
    print foo("Shayne")
    
    $ python hello.py
    ShayneBrozu
    

    递归

    递归简单地说就是函数调用自身的程序结构,通常用来解决了一些能够由大化小,规律一致的问题。

    两个经典:阶乘和幂

    n的阶乘数学表示为 n * (n - 1) * (n - 2) ... * 2 * 1, 函数递归的表现形式如下:

    def factorial(n):
        if n == 1:
            return 1
        else:
            return n * factorial(n - 1)
    print factorial(10)
    
    $ python hello.py
    3628800
    

    另一个常用的例子是幂,我们要计算x的n次幂可能会是这样的:

    def  power(x, n):
        if n == 0:
            return 1
        else:
            return x * power(x, n - 1)
    
    print power(10, 3)
    
    $ python hello.py
    1000
    

    当然你也可以用循环实现上述代码,甚至效率更加高,但是编程很重要一点是要追求代码的易读,尤其当程序代码量大的时候尤其重要。

    另一个经典:二元查找

    假定要你查找某个数字是否在某序列中,你可以通过不断取中间值来和查找值进行比较来得到最终的结果(当然要先排序)。如下是一个实现例子:

    def  search(sequence, number, left, right):
        midd = (left + right) / 2
        if left == right:
            assert number == sequence[midd]
            return midd
        elif sequence[midd] > number:
            return search(sequence, number, left, midd - 1)
        else:
            return search(sequence, number, midd, right)
    
    seq = [34, 67, 15, 28, 89, 44, 9, 66]
    seq.sort()
    print search(seq, 67, 0, len(seq))
    
    
    $ python hello.py
    6
    

    相关文章

      网友评论

        本文标题:《Python基础教程》第6章 抽象

        本文链接:https://www.haomeiwen.com/subject/vyuqxttx.html