第六章 抽象
1. 函数 def functionname(para):
用于def后面的' '添加文档字符串,相当于注释#
>>> def fibs(num):
'Get fibonaqi 这里的字符串用来注释,一般说明函数功能'
result = [0, 1]
for i in range(num - 2):
result.append(result[-1] + result[-2])
return result
>>> fibs(10)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
使用help()可以查询函数的文档字符串
>>> help(fibs)
Help on function fibs in module __main__:
fibs(num)
Get fibonaqi 这里的字符串用来注释,一般说明函数功能
>>>
return后不加值,只表示函数的结束,而没有返回值,这样可以避免应该返回序列时,意外返回None
>>> def test():
print "test"
return #无返回值
print "hello" #不执行
>>> x = test()
test
2. 参数对外部变量影响
函数内给参数赋值,不会改变外部变量的值,参数存储在局部作用域中
>>> def try_to_change(n):
n = 3
>>> a = 4
>>> try_to_change(a) #虽然在函数内部重新赋值,但外部不变, n和a实际上是完全不同的变量,
>>> a
4
但是对于可改变的数据结构,如列表,参数的内部赋值会改变外部变量的值
内部参数与外部变量指向同一个列表,所以会被修改
若不想改变外部列表,可以传进一个副本
>>> def change(n):
n[0] = 'test'
>>> names = ['Hello', 'world']
>>> change(names)
>>> names
['test', 'world']
>>> #采用普通方法进行模拟
>>> names = ['Hello', 'world']
>>> n = names #模拟传参
>>> n[0] = 'test' #改变列表
>>> names
['test', 'world']
完整示例——存储名字,并能用名字、中间名或姓来查找联系人
若名字为'Magus Lie Hetland'存储格式类似
data = {
'first': { 'Magus': 'Magus Lie Hetland'},
'middle': {'Lie': 'Magus Lie Hetland'},
'last': {'Hetland': 'Magus Lie Hetland'}
}
注意insert(index,value)函数,在列表的索引位置插入值
>>> def init(data): #data作为存储表,初始化
data['first'] = {}
data['middle'] = {}
data['last'] = {}
>>> def store(data, full_name): #存储,将全名存储到表中
names = full_name.split() #将名字按空格(即first,middle,last)分开,返回列表,如'Ma Li He'返回['Ma', 'Li', 'He']
if len(names) == 2: names.insert(1, '')#若无中间名,则插入空来表示中间名['Mr', 'Zha']返回['Mr', '', 'Zha']
labels = 'first', 'middle', 'last' #元组
for label, name in zip(labels, names): #元组与序列间也可使用zip
people = lookup(data, label, name)
if people:
people.append(full_name)
else:
data[label][name] = [full_name] #当键不存在时,自动添加键值对,
#但如果输出不存在键对应值,则报错
>>> def lookup(data, label, name): #查找,根据label查找是name的中间人
return data[label].get(name)
>>> MyNames = {}
>>> init(MyNames)
>>> store(MyNames, 'Magnus Lie Hetland')
>>> lookup(MyNames, 'middle', 'Lie')
['Magnus 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']
例2. 不可变的数字和可改变的参数
>>> def inc(x): return x + 1
>>> foo = 10
>>> inc(foo)
11
>>> foo #外部变量未发生变化
10
>>> foo = inc(foo) #将foo重新赋值
>>> foo
11
使用列表外部变量foo改变了
>>> def inc(x): x[0] = x[0] + 1
>>> foo = [10]
>>> inc(foo)
>>> foo
[11]
3. 关键字参数和默认值
位置:是指根据参数的对应位置传参,如def a(a,b,c):,调用a(1,2,3),1传给a,2传给b,3传给c,这样参数位置容易记混。
关键字参数,适用于大规模程序,清晰
>>> def hello(name, greeting):
print '%s, %s!' %(greeting, name)
>>> hello('sun', 'Hello') #位置参数
Hello, sun!
>>> hello(name = 'Sun', greeting = 'Hello') #关键字参数
Hello, Sun!
>>> hello(greeting = 'Hello', name = 'Sun') #关键字参数,不必关心位置
Hello, Sun!
默认值
>>> def hello(name = 'world', greeting = 'Hello'):
print '%s, %s!' %(greeting, name)
>>> hello()
Hello, world!
>>> hello('Sun')
Hello, Sun!
>>> hello(greeting = 'Hi')
Hi, world!
位置参数与关键字参数混用,将位置参数放在前面。尽量避免这么用,容易引起混乱。
>>> def hello(name, greeting = 'Hello', punc = '!'):
print '%s, %s%s' %(greeting, name, punc)
>>> hello('Sun')
Hello, Sun!
>>> hello('Sun', 'Hi')
Hi, Sun!
>>> hello('Sun', punc = '..')
Hello, Sun..
>>> hello() #因为name是必须要有,若有默认值,则可没有
Traceback (most recent call last):
File "<pyshell#385>", line 1, in <module>
hello()
TypeError: hello() takes at least 1 argument (0 given)
>>>
4. 收集参数——在定义时使用*或**,用来收集参数,允许使用不定数量的参数
*:收集其余的位置参数并作为元组 返回
>>> def print_params2(title, *params):
print title
print params
>>> print_params2('Param:', 1, 2, 3)
Param:
(1, 2, 3) #以元组形式返回
>>> print_params2('Param:') #不提供收集元素时,返回空元组
Param:
()
>>> print_params2('Param:', 1)
Param:
(1,) #只有一个元素时,仍为元组
**:收集其余的关键字参数并作为字典 返回,可与其他混用
>>> def print_params3(x, y, z = 3, *pospar, **keypar):
print x, y, z
print pospar
print keypar
>>> print_params3(1,2,3,4,5,6,7, fool = 1, bar = 2)
1 2 3
(4, 5, 6, 7)
{'fool': 1, 'bar': 2}
>>> print_params3(1,2)
1 2 3
()
{}
5. 收集参数的翻转过程——在调用时使用*或**,将参数分配到定义的参数中,用于字典或列表分割时
用于列表
>>> def add(x,y): return x + y
>>> params = (1,2)
>>> add(*params)
3
用于字典
>>> def hello(name, greeting):
print '%s, %s!' %(greeting, name)
>>> params = {'name': 'Sir Robin', 'greeting': 'Welcome'}
>>> hello(**params)
Welcome, Sir Robin!
参数使用实例
#模拟步长大于0的range()
>>> interval(10)
start = 0 stop = 10 step = 1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> def interval(start, stop=None, step=1):
'Imitates range() for step > 0'
if stop is None: #若未给stop指定值
start, stop = 0, start #多个赋值,0赋值给start,start的值赋值给stop
result = []
i = start
while i < stop:
result.append(i)
i += step
return result
#
>>> def story(**kwds):
return '%(job)s called %(name)s.' %kwds
>>> def power(x, y ,*others):
if others:
print 'Received redundant parameters:',others
return pow(x,y)
#使用
>>> params = {'job':'language','name':'python'}
>>> print story(**params) #调用时分割字典,定义中收集
language called python.
>>> del params['job']
>>> print story(job='test', **params)
>>> power(2,3,'test')
Received redundant parameters: test
>>> params = (5,) * 2 #即(5,5)
>>> power(*params) #先分割,在赋给x,y
3125
6.作用域
x = 1, 将名字x引用到值1上,类似字典
内建函数vars()返回这个字典
>>> x = 1
>>> scope = vars()
>>> scope['x']
1
>>> scope['x'] += 1 #一般情况下,vars()返回的字典不能修改
>>> x
2
局部变量,全局变量
函数内部访问全局变量,慎用!
>>> def com(para): print para + external
>>> external = 'external'
>>> com('param ')
param external
若全局变量与局部变量名字相同,会被局部变量覆盖,可使用global()类似vars(),获得全局变量的字典
>>> def com(para): print para + globals()['para']
>>> para = 'berry'
>>> com('test ')
test berry
重绑定全局变量,将变量引用到其他新值——函数内部声明全局变量
>>> x = 1
>>> def change_global():
global x
x = x + 1
>>> change_global()
>>> x
2
嵌套作用域——函数中定义函数,例如闭包
外部作用域中的变量一般不能被改变,但是用闭包,每次调用外层函数,内部函数都会被重新绑定,也即外部作用域factor每次都有一个新值
>>> def multiplier(factor):
def multiplyByFactor(number):
return number * factor
return multiplyByFactor #返回一个函数,这时并未调用
>>> double = multiplier(2) #double是一个函数
>>> double #double是一个函数
<function multiplyByFactor at 0x0214C6F0>
>>> double(5) #调用multiplyByFactor(number)
10
>>> multiplier(2)(5) #效果同上
10
7. 递归——每调用一个函数,都会创建一个新的命名空间,意味着当函数调用自身时,实际上调用的是两个不同的函数
阶乘
>>> def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
>>> factorial(5)
120
幂
>>> def power(x, n):
if n == 0:
return 1
else:
return x * power(x, n - 1)
>>> power(2,3)
8
递归实例——二元搜索
前提:排好序
若上下限相同,则那就是数字所在位置,返回;
否则,找到两者的中间,查找数字是在左侧还是右侧,继续查找数字所在的那半部分。
>>> def search(sequence, number, lower = 0, upper = None):
if upper is None: upper = len(sequence) - 1
if lower == upper:
assert number == sequence[upper]
return upper
else:
middle = (lower + upper) // 2
if number > sequence[middle]:
return search(sequence, number, middle + 1, upper)
else:
return search(sequence, number, lower, middle)
>>> seq = [34, 67, 8, 123, 4, 100, 95]
>>> seq.sort()
>>> seq
[4, 8, 34, 67, 95, 100, 123]
>>> search(seq, 34)
2
总结:
元组输出格式化,直接使用键,而不需要加引号
>>> d = {'a':1, 'b':2}
>>> print '%(a)s corresponds to %(b)s.' %d #注意a有括号,无引号
1 corresponds to 2.