《Python基础教程》学习笔记 Chp6 抽象

来源:互联网 发布:箪食壶浆以迎将军者乎 编辑:程序博客网 时间:2024/05/19 15:42

1.创建函数
函数是可以调用的(可能带有参数,也就是放在圆括号中的值),它执行某种行为并且返回一个值。一般来说,内建的callable函数可以用来判断函数是否可调用:

>>> import math>>> x = 1>>> y = math.sqrt>>> callable(x)False>>> callable(y)True

函数定义
使用def(或“函数定义”)语句即可,后面带圆括号,以冒号结尾。例:

>>> def hello(name) :...     return 'hello, ' + name + "!"... >>> print hello('world')hello, world!>>> print hello('Alice')hello, Alice!

文档化函数
如果在函数的开头写下字符串,它就会作为函数的一部分进行存储,这称为文档字符串。例:

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

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

>>> square.func_doc'calculates the square of the number x.'

并非正真函数的函数
Python有些函数没有return语句,或者虽有return语句但return后面没有跟任何,这些函数都不会返回值。例:

>>> def test():...     print 'This is printed !'...     return...     print 'This is not !'...     >>> x = test()This is printed !>>> x>>> print xNone

注:return语句只起到结束函数的作用。用print语句打印x的值是None。所有函数都会有返回值,当不需要他们返回值的时候,他们就返回None。

2.参数魔法
形参:写在def语句中函数名后面的变量通常叫做函数的形参。
实参:调用函数的时候提供的值叫做实参,或者称为参数。
为什么要修改参数
使用函数改变数据结构(例如列表或者字典)是一种将程序抽象化的好方法。
例:假设要编写一个存储名字并且能根据名字、中间名、或姓查找联系人的程序,可以使用下面的数据结构:

>>> storage ={}>>> storage['first'] = {}>>> storage['middle'] ={}>>> storage['last'] = {}

如果要把我的名字加入到这个数据结构中,可以像下面这样做:

>>> me = {'Nagnus Lie Hetland'}>>> storage['first']['Nagnus'] = me>>> storage['middle']['Lie'] = me>>> storage['last']['Hetland'] = me

现在如果想得到素有注册的中间名为Lie的人,可以像下面这么做:

>>> storage['middle']['Lie']set(['Nagnus Lie Hetland'])

如果要加入其它名字到列表中,会很麻烦,这时函数就可以提高效率了。抽象的要点就是隐藏更新时繁琐的细节,上面初始化数据结构的例子可以通过函数实现,如下:

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

再写个获取名字的函数,如下:

>>> def lookup(data, lable, name) :...     return data[lable].get(name)... >>> lookup(storage, 'middle', 'Lie')set(['Nagnus Lie Hetland'])

最后写存储名字的函数,如下:

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

运行结果如下所示:

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

关键字参数和默认值
之前的例子中使用的参数都是位置参数,因为他们的位置很重要,事实上比他们的名字更加重要。我们现在要引入的这个功能可以回避位置问题。考虑下面两个函数:

>>> def hello(greeting, name):...     print '%s, %s !' %(greeting, name)...     >>> def hello1(name, greeting):...     print '%s, %s !' %(name, greeting)...     >>> hello('Hello', 'world')Hello, world !>>> hello1('Hello', 'world')Hello, world !

以上两个代码实现的功能是完全一样的,只是参数顺序反过来了。有写时候(尤其是参数很多的时候),参数的顺序是很难记住的。为了让事情简单些,可以提供参数的名字。如下所示:

>>> hello(greeting = 'Hello', name = 'world')Hello, world !>>> hello(name = 'world', greeting = 'Hello')Hello, world !

这样一来,顺序就完全没影响了。但是参数名和值一定要对应:

>>> hello1(greeting = 'Hello', name = 'world')world, Hello !

这类使用参数名提供的参数叫做关键字参数。它的主要作用在于可以明确每个参数的作用。可以避免奇怪的函数调用。关键字参数最厉害的地方在于可以在函数中给参数提供默认值。当参数具有默认值的时候,调用的时候就可以不提供、提供一些或提供所有的参数。例:

>>> def hello2(greeting = 'Hello', name = 'world'):...     print '%s, %s !' %(greeting, name)...     >>> hello2()Hello, world !>>> hello2(greeting = 'Greetings')Greetings, world !>>> hello2(greeting = 'Greetings', name = 'world')Greetings, world !

位置参数和关键字参数是可以联合使用的。把位置参数放置在前面就可以了。如果不这样做,解释器会不知道它们到底谁是谁(也就是它们应该处的位置)。例如:

>>> def hello3(name, greeting = 'Hello', punctuation = '!'):...     print '%s %s%s' %(name, greeting, punctuation)...     >>> hello3('Mars')Mars Hello!>>> hello3('Mars', 'Howdy')Mars Howdy!>>> hello3('Mars', 'Howdy', '...')Mars Howdy...>>> hello3('Mars', greeting = 'ni hao !')Mars ni hao !!>>> hello3()Traceback (most recent call last):  File "<interactive input>", line 1, in <module>TypeError: hello3() takes at least 1 argument (0 given)

收集参数
用户可以给函数提供任意多的参数,在参数前面加个星号*就可以了,参数前的星号将所有值放置在同一个元组中,可以说是。例如:

>>> def print_params(*params) :...     print params...     >>> print_params(1,2,3)(1, 2, 3)

星号的意思就是“收集其余的位置参数”。如果不提供任何收集的元素,params就是个空元组。例:

>>> print_params1('Params : ', 1,2,3)Params : (1, 2, 3)>>> print_params1('Nothing ! ')Nothing ! ()

参数收集也可以处理关键字参数,使用两个星号。当收集位置参数和收集关键字参数一起使用时,*param收集的是位置参数部分的,两个星号收集的是关键字参数部分的。例:

>>> def print_params2(**params):...     print params...     >>> print_params2(x=1,y=2,z=3){'y': 2, 'x': 1, 'z': 3}>>> def print_params2(x, y, z=3, *paspar, **keypar):...     print x, y, z...     print paspar...     print keypar...     >>> print_params2(1,2,3,5,6,7,foo=1,bar=2)1 2 3(5, 6, 7){'foo': 1, 'bar': 2}>>> print_params2(1,2)1 2 3(){}>>> print_params2(1,2,3,5,6,7,8,9,foo=1,bar=2,foo1=3,bar1=4)1 2 3(5, 6, 7, 8, 9){'bar1': 4, 'foo': 1, 'bar': 2, 'foo1': 3}

参数收集的逆过程
刚刚已经介绍过收集参数了,那现在来介绍参数的分配,同样适用*和双星号运算符——不过是在调用而不是在定义的时候适用。
使用*运算符处理参数列表,例:

>>> def add(x,y):...     return x+y... >>> params = (1,2)>>> add(*params)3

使用**运算符处理字典参数,例:

>>> def hello2(greeting = 'Hello', name = 'world'):...     print '%s, %s !' %(greeting, name)...     >>> params = {'name': 'Sir Robin', 'greeting' : 'Well met'}>>> hello2(**params)Well met, Sir Robin !

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

>>> def story(**kwds):...     return 'once upon a time , there was a '\...         '%(job)s called %(name)s.' %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        #指定参数(序列解包)...     result = []...     i = start...     while i < stop:...         result.append(i)...         i+=step...     return result... >>> print story(job = 'king', name = 'Gumby')once upon a time , there was a king called Gumby.>>> params = {'job':'language', 'name':'Pythone'}>>> print story(**params)once upon a time , there was a language called Pythone.>>> del params['job']>>> params{'name': 'Pythone'}>>> print story(job='stroke of genius',**params)once upon a time , there was a stroke of genius called Pythone.>>> params{'name': 'Pythone'}>>> 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]>>> interval(1,5)[1, 2, 3, 4]>>> interval(3,12,4)[3, 7, 11]>>> power(*interval(3,7))Received redundant parameters :  (5, 6)81

3.作用域
变量和所对应的值相当是个‘不可见’的字典,这类‘不可见’的字典叫做命名空间或者作用域。
屏蔽引发的问题
如果局部变量或者参数的名字和想要访问的全局变量名相同的话,就不能直接访问了。全局变量会被局部变量屏蔽。如果的确需要的话,可以使用globals函数获取全局变量,它可以返回全局变量的字典(locals返回局部变量的字典)。例:

>>> def combine(parameter):...     print parameter + globals()['parameter']...     >>> parameter = 'berry'>>> combine('shrub')shrubberry

嵌套作用域
Python的函数是可以嵌套的,也就是说可以将一个函数放在另一个里面。例:

>>> def foo():...     def bar():...         print 'hello , world !' ...     bar()...     >>> foo()hello , world !

嵌套一般来说并不是很重要,但它有一个很突出的应用,例如需要用一个函数“创建”另一个,也就意味着可以像下面这样(在其他函数内)书写函数:

>>> def multiplier(factor):...     print factor...     def multiplyByFactor(number):...         print 'number is : ', number, ' , factor is : ', factor...         return number * factor...     return multiplyByFactor... >>> double = multiplier(2)2>>> double(5)number is :  5  , factor is :  210>>> multiplier(5)(4)5number is :  4  , factor is :  520

分析说明:一个函数位于另外一个里面,外层函数返回里层函数。也就是说函数本身被返回了,但并没有被调用。重要的是返回的函数还可以访问它的定义所在的作用域。每次调用外层函数,它内部的函数都被重新绑定。
递归
使用递归实现阶乘:

>>> def factorial(n):...     if n == 1:...         return 1...     return n * factorial(n-1)... >>> factorial(5)120>>> factorial(1)1

使用递归实现幂:

>>> def power(x, n):...     if n==0:...         return 1...     return x * power(x, n-1)... >>> power(3,5)243>>> power(3,0)1

使用递归实现二分法查找:

>>> def search(sequence, number, lower = 0, upper = None):...     if upper is None : upper = len(sequence) - 1...     if lower == upper :...         print 'sequence[upper] is : ', sequence[upper]...         assert number == sequence[upper]...         return upper...     else :...         middle = (lower + upper) / 2...         print 'middle is : ', middle, ' ; sequence[middle] is : ', sequence[middle]...         if number > sequence[middle] :...             return search(sequence, number, middle + 1, upper)...         else :...             return search(sequence, number, lower, middle)...         >>> sequence = [34,67,8,123,4,100,95]>>> sequence.sort()>>> sequence[4, 8, 34, 67, 95, 100, 123]>>> search(sequence, 34)middle is :  3  ; sequence[middle] is :  67middle is :  1  ; sequence[middle] is :  8middle is :  2  ; sequence[middle] is :  34sequence[upper] is :  342>>> search(sequence, 95)middle is :  3  ; sequence[middle] is :  67middle is :  5  ; sequence[middle] is :  100middle is :  4  ; sequence[middle] is :  95sequence[upper] is :  954

函数式编程
map
map函数可以将序列中的元素全部传递给一个函数。例:

>>> map(str, range(10))['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

filter
filter函数可以基于一个返回布尔值的函数对元素进行过滤。例:

>>> def func(x):...     return x.isalnum()... >>> seq = ['faa', 'fafe', '###', '***']>>> filter(func, seq)['faa', 'fafe']

上述的map和filter函数都可以用列表推导式代替。
reduce
reduce函数会将序列的前两个元素与给定的函数联合使用,并且将它们的返回值和第三个元素继续联合使用,知道整个序列处理完毕,并且得到一个最终结果。
例:计算一个序列的数字的和,可以使用reduce函数加上lambda(可以创建短小的函数)x,y:x + y(继续使用相同的数字):

>>> nums = [1,2,3,4,5]... >>> reduce(lambda x, y : x + y, nums)15

这里lambda x, y : x + y其实就是创建了x与y的和的短小函数。

4.本章新函数
map(func,seq[, seq, …]) 对序列中的每个元素应用函数
filter(func, seq) 返回其函数为真的元素的列表
reduce(func, seq[, initial]) 等同于func(func(func(seq[0], seq[1], seq[2], …)))
sum(seq) 返回seq中所有元素的和
apply(func[,args[, kwargs]]) 调用函数,可以提供参数

0 0
原创粉丝点击