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

Posted lzh398651363

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Python基础教程》学习笔记 Chp6 抽象相关的知识,希望对你有一定的参考价值。

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 x
None

注: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 :  2
10
>>> multiplier(5)(4)
5
number is :  4  , factor is :  5
20

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

>>> 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 :  67
middle is :  1  ; sequence[middle] is :  8
middle is :  2  ; sequence[middle] is :  34
sequence[upper] is :  34
2
>>> search(sequence, 95)
middle is :  3  ; sequence[middle] is :  67
middle is :  5  ; sequence[middle] is :  100
middle is :  4  ; sequence[middle] is :  95
sequence[upper] is :  95
4

函数式编程
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]]) 调用函数,可以提供参数

以上是关于《Python基础教程》学习笔记 Chp6 抽象的主要内容,如果未能解决你的问题,请参考以下文章

读书笔记--《Python基础教程第二版》--第七章 更加抽象

学习笔记Python基础教程学习笔记

[Python学习笔记1]Python语言基础 数学运算符 字符串 列表

学习笔记之Python 基础教程

《Python基础教程》第20章学习笔记

Python基础教程 第六章 学习笔记