Python学习7——抽象


将语句组合成函数,可以告诉计算机如何完成任务,且只需说一次,而不用反复向计算机传达详细指令。

斐波那契数(一种数列,其中每个数都是前两个数的和):

>>> fibs = [0,1]
>>> for i in range(10):
    fibs.append(fibs[-2]+fibs[-1])

    
>>> fibs
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

优化一下代码,使其指定动态的范围:

>>> fibs = [0,1]
>>> num = input('enter:')
enter:10
>>> for i in range(int(num)-2):
    fibs.append(fibs[-2]+fibs[-1])

    
>>> fibs
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

如果不从用户那里读取数字,而是通过参数来获取,可以继续优化代码:

>>> def fibs(num):
    re = [0,1]
    for i in range(num-2):
        re.append(re[-2]+re[-1])
    return re

>>> fibs(10)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

def语句定义函数,return语句用于从函数返回值。num是一个形参,可以在其他地方多次调用fibs(),而不用每次都要将代码再敲一遍。

除了#注释之外,还有一种编写注释的方式,就是添加独立的字符串。放在函数开头的字符串称为文档字符串。

>>> def square(x):
    'Calculates the square of the number x.'
    return x * x

>>> square.__doc__     #访问文档字符串
'Calculates the square of the number x.'

__doc__是函数的属性。

>>> help(square)    #内置函数help可以获取有关函数的信息
Help on function square in module __main__:

square(x)
    Calculates the square of the number x.

修改参数

当参数为可变的数据结构(列表)时,在函数内修改参数,在外面调用函数。

>>> def change(n):
    n[0] = 'Mr.Gumby'

    
>>> names = ['Mrs.Entity','Mrs.Thing']
>>> change(names)
>>> names
['Mr.Gumby', 'Mrs.Thing']

上面栗子也可以这么写:

>>> names = ['Mrs.Entity','Mrs.Thing']
>>> n = names      #假装传递名字作为参数
>>> n[0] = 'Mr.Gumby'     #修改列表,这步上面是在函数内进行的
>>> names
['Mr.Gumby', 'Mrs.Thing']    

将同一个列表赋给两个变量时,这两个变量将同时指向这个列表。想避免这种结果,必须创建列表的副本。

>>> names = ['Mrs.Entity','Mrs.Thing']
>>> n = names[:]    #对序列进行切片操作时,返回的切片是副本。现在n和names包含2个相等但不同的列表。这里也可以用names.copy()来创建副本。
>>> n[0] = 'Mr.Gumby'
>>> names
['Mrs.Entity', 'Mrs.Thing']
>>> n
['Mr.Gumby', 'Mrs.Thing']

将切片参数传给函数,也不会影响:

>>> def change(n):
    n[0] = 'Mr.Gumby'

    
>>> names = ['Mrs.Entity','Mrs.Thing']
>>> change(names[:])
>>> names
['Mrs.Entity', 'Mrs.Thing']

栗子:假设编写一个程序,让它储存姓名,并让用户能够根据名字,中间名或姓找人。

创建初始化数据结构的函数:

>>> def init(data):
    data['first'] = {}
    data['middle'] = {}
    data['last'] = {}

    
>>> storage = {}
>>> init(storage)   #这个函数承担了初始化职责,提高了代码的可读性。
>>> storage
{'first': {}, 'middle': {}, 'last': {}}

然后需要一个函数来获取人员姓名:

>>> def lookup(data,label,name):
    return data[label].get(name)

接着要将人员存储到数据结构中:

>>> def store(data,full_name):    #将参数data和full_name提供给这个函数。
    names = full_name.split()   #通过拆分full_name创建1个名为names的列表。
    if len(names) == 2 : names.insert(1,'')  #如果只有名字和姓,没有中间名,就将中间名设为空字符串
    labels = 'first','middle','last'    #将label存储到元组中(也可以是列表)
    for name,label in zip(names,labels):   #zip函数将label和name合并,可以对label-name对执行一些操作
        people = lookup(data,label,name) 
        if people:
            people.append(full_name)  #将full_name插入一个新列表
        else:
            data[label][name]=[full_name]

现在运行看看:

>>> me = {}
>>> init(me)
>>> me
{'first': {}, 'middle': {}, 'last': {}}
>>> store(me,'Magnus Lie Hetland')
>>> me
{'first': {'Magnus': ['Magnus Lie Hetland']}, 'middle': {'Lie': ['Magnus Lie Hetland']}, 'last': {'Hetland': ['Magnus Lie Hetland']}}
>>> lookup(me,'middle','Lie')
['Magnus Lie Hetland']
>>> store(me,'Robin Hood')
>>> store(me,'Robin Locksley')
>>> store(me,'Mr. Gumby')
>>> lookup(me,'first','Robin')
['Robin Hood', 'Robin Locksley']
>>> lookup(me,'middle','')
['Robin Hood', 'Robin Locksley', 'Mr. Gumby']

关键字参数

当函数需要传多个参数时,参数的位置就变的非常重要,比名字还重要。

>>> def hello_1(greeting,name):
    print('{},{}!'.format(greeting,name))

    
>>> def hello_2(name,greeting):
    print('{},{}!'.format(name,greeting))

    
>>> hello_1('hello','world')
hello,world!
>>> hello_2('hello','world')
hello,world!

为了避免遗忘参数的位置,可以用关键字参数(用名称指定参数):

>>> hello_1(greeting='hello',name='world')
hello,world!
>>> hello_2(greeting='hello',name = 'world')
world,hello!

给参数指定默认值:

>>> def hello_3(greeting = 'hello',name='world'):   #给参数指定默认值后,调用函数时可不提供它。
    print('{},{}!'.format(greeting,name))

    
>>> hello_3()   #不提供参数
hello,world!
>>> hello_3('Greetings','universe')    #提供全部参数值
Greetings,universe!
>>> hello_3(name='Gumby')    #提供部分参数值

收集参数

>>> def print_params(*params):  #参数前面的星号将提供的所有值都放在一个元组中,也就是将这些值收集起来。
    print(params)

    
>>> print_params('Testing')
('Testing',)
>>> print_params(1,2,3)
(1, 2, 3)
>>> x,*y,z = 1,2,3,4,5,6   #上面的情况与赋值时的星号有点相似,不过赋值时带星号的变量收集多余的值
>>> x,y,z
(1, [2, 3, 4, 5], 6)    #并且是将收集的值放在列表中
>>> def print_params_2(title,*params):
    print(title)
    print(params)

    
>>> print_params_2('Params:',1,2,3)   #星号意味着收集余下的位置参数
Params:
(1, 2, 3)
>>> print_params_2('Nothing:')   #如果没有可收集的参数,params将是一个空元组
Nothing:
()

当带星号的参数位置不在末尾:

>>> def in_the_middle(x,*y,z,f):
    print(x,y,z,f)

    
>>> in_the_middle(1,2,3,4,5,z=6,f=7)   #需要使用名称来指定后续参数
1 (2, 3, 4, 5) 6 7
>>> in_the_middle(1,2,3,4,5,6,7)
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in <module>
    in_the_middle(1,2,3,4,5,6,7)
TypeError: in_the_middle() missing 2 required keyword-only arguments: 'z' and 'f'

单个星号不会收集关键字参数,要收集关键字参数,可使用两个星号:

>>> def print_params_3(**params):
    print(params)

    
>>> print_params_3(x=1,y=2,z=3)    #两个星号得到的是一个字典
{'x': 1, 'y': 2, 'z': 3}
>>> def print_params_4(x,y,z=3,*pospar,**keypar):
    print(x,y,z)
    print(pospar)
    print(keypar)

    
>>> print_params_4(1,2,3,4,5,6,foo=1,bar=2)
1 2 3
(4, 5, 6)
{'foo': 1, 'bar': 2}

结合这些技术,可以将上面存储姓名的栗子再次优化:

>>> def store(data,*full_names):
    for full_name in full_names:
        names = full_name.split()
        if len(names)==2:names.insert(1,'')
        labels = 'first','middle','last'
        for name,label in zip(names,labels):
            people = lookup(data,label,name)
            if people:
                people.append(full_name)
            else:
                data[label][name] = [full_name]

                
>>> init(me)
>>> store(me,'Luke Skywalker','Anakin Skywalker')
>>> lookup(me,'first','Luke')
['Luke Skywalker']

优化之后,store里可以一次存储多个名字。

分配参数

>>> def hello(greeting='hello',name='Toney'):
    print('{},{}!'.format(greeting,name))

    
>>> params = {'name':'Mary','greeting':'Greet'}
>>> hello(**params)
Greet,Mary!

通过使用运算符**可将字典中的值分配给关键字参数。

只有在定义函数(允许可变数量的参数)或调用函数时(拆分字典或序列)使用,星号才能发挥作用。

综合的栗子:

>>> def story(**kwds):
    return 'Once upon a time,there was a '\
           '{job} called {name}'.format_map(kwds)

>>> def 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:   #如果没有给参数stop指定值
        start,stop = 0,start    #就调整参数start和stop的值

    result = [] 
    i = start                #从start开始往上数
    while i < stop:        #数到stop位置
        result.append(i)     #将当前数的数加到result列表末
        i += step              # i=i+1
    return result

>>> print(story(job='king',name='Gumby'))
Once upon a time,there was a king called Gumby
>>> print(story(name='Sir Robin',job='brave knight'))
Once upon a time,there was a brave knight called Sir Robin
>>> params = {'job':'language','name':'Python'}            
>>> print(story(**params))
Once upon a time,there was a language called Python
>>> power(2,3)
8
>>> power(y=3,x=2)
8
>>> params = (5,)*2
>>> power(*params)
3125
>>> power(3,3,'hello,world')
Received redundant parameters: ('hello,world',)
27
>>> interval(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

作用域

当局部变量中有一个与全局变量命名相同的变量时,可用函数globals来访问全局变量。

>>> def combine(paramter):
    print(paramter + globals()['paramter'])

    
>>> paramter = 'berry'
>>> combine('Shrub')
Shrubberry

在函数内给变量赋值时,该变量默认是局部变量,但可以指定该变量是全局变量。

>>> x = 1
>>> def change():
    global x
    x = x+1

    
>>> change()
>>> x
2

递归

函数可调用自身,称为递归。可使用递归完成的任何任务都可使用循环来完成,但有时使用递归函数的可读性更高。

>>> def fac(n):
    result = n   #将result设置为n
    for i in range(1,n):
        result *= i    #依次乘以1到n-1的每个数字
    return result    #返回result

>>> fac(5)
120
>>> def fac(n):
    if n == 1:
        return 1
    else:
        return n * fac(n-1)

    
>>> fac(5)
120

上面两个函数的功能一致,都是计算数字n的阶乘,貌似在这种场景下,递归的可读性比较高。

优质内容筛选与推荐>>
1、关于IO操作的阻塞,非阻塞,异步,同步的简明解释
2、BZOJ1807 : [Ioi2007]Pairs 彼此能听得见的动物对数
3、分页式内核模式驱动--解释#ifdef ALLOC_PRAGMA代码段的原理
4、if ()
5、python-day3(正式学习)


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn