Python快速學習第五天
第五天:抽象
1、 函式
檢測函式是否可呼叫:callable
>>> import math
>>> y=math.sqrt
>>> callable(y)
True
>>> x=1
>>> callable(x)
False
注意:Python3.0不在支援callable,需要使用表示式hasattr(func,_call_call_)代替
建立函式:def functionname(params):
>>>def fibs(num):
... 'Get fibonaqi sequnce! '
... result=[0,1]
... for i in range(num-2):
... result.append(result[-2]+result[-1])
... return result
...
>>>fibs(10)
[0, 1,1, 2, 3, 5, 8, 13, 21, 34]
用於def後面的'Getfibonaqi sequnce! '新增文件字串,相當於註釋#,使用help()可以查詢函式的文件字串
help(fibs)
Help on function fibs in module __main__:
fibs(num)
Getfibonaqi sequnce!
...skipping...
Help on function fibs in module __main__:
fibs(num)
Getfibonaqi sequnce!
return後不加值,只表示函式的結束,而沒有返回值,這樣可以避免應該返回序列時,意外返回None
>>> def test():
... print 'tanggao'
... return
... print 'tanggao isgood'
...
>>> x=test()
tanggao
>>> print x
None
2、 引數對外部變數影響
函式內給引數賦值,不會改變外部變數的值,引數儲存在區域性作用域中
>>> def try_to_change(n):
... n=3
...
>>> t=4
>>> try_to_change(t)#雖然在函式內部重新賦值,但外部不變
>>> t
4
但是對於可改變的資料結構,如列表,引數的內部賦值會改變外部變數的值
內部引數與外部變數指向同一個列表,所以會被修改
若不想改變外部列表,可以傳進一個副本
>>> def change(n):
... n[0]='tanggao'
...
>>> names=['hello','world']
>>> change(names)
>>> names
['tanggao', 'world']
>>> #採用普通方法進行模擬
...
>>> names=['hello','world']
>>> n=names
>>> n[0]='tanggao'
>>> names
['tanggao', 'world']
完整示例——儲存名字,並能用名字、中間名或姓來查詢聯絡人
若名字為'MagusLie Hetland'儲存格式類似
data = {
'first':{ 'Magus': 'Magus Lie Hetland'},
'middle':{'Lie': 'Magus Lie Hetland'},
'last':{'Hetland': 'Magus Lie Hetland'}
}
注意insert(index,value)函式,在列表的索引位置插入值
Python程式碼
1. >>> def init(data): #data作為儲存表,初始化
2. data['first'] = {}
3. data['middle'] = {}
4. data['last'] = {}
5.
6. >>> def store(data, full_name): #儲存,將全名儲存到表中
7. names = full_name.split() #將名字按空格(即first,middle,last)分開,返回列表,如'Ma Li He'返回['Ma', 'Li', 'He']
8. if len(names) == 2: names.insert(1, '')#若無中間名,則插入空來表示中間名['Mr', 'Zha']返回['Mr', '', 'Zha']
9. labels = 'first', 'middle', 'last' #元組
10. for label, name in zip(labels, names): #元組與序列間也可使用zip
11. people = lookup(data, label, name)
12. if people:
13. people.append(full_name)
14. else:
15. data[label][name] = [full_name] #當鍵不存在時,自動新增鍵值對,
16. #但如果輸出不存在鍵對應值,則報錯
17.
18. >>> def lookup(data, label, name): #查詢,根據label查詢是name的中間人
19. return data[label].get(name)
20.
21.
22. >>> MyNames = {}
23. >>> init(MyNames)
24. >>> store(MyNames, 'Magnus Lie Hetland')
25. >>> lookup(MyNames, 'middle', 'Lie')
26. ['Magnus Lie Hetland']
27. >>> store(MyNames, 'Robin Hood')
28. >>> store(MyNames, 'Robin Locksley')
29. >>> lookup(MyNames, 'first', 'Robin')
30. ['Robin Hood', 'Robin Locksley']
31. >>> store(MyNames, 'Mr. Gumby')
32. >>> lookup(MyNames, 'middle', '')
33. ['Robin Hood', 'Robin Locksley', 'Mr. Gumby']
例2. 不可變的數字和可改變的引數
Python程式碼
1. >>> def inc(x): return x + 1
2.
3. >>> foo = 10
4. >>> inc(foo)
5. 11
6. >>> foo #外部變數未發生變化
7. 10
8. >>> foo = inc(foo) #將foo重新賦值
9. >>> foo
10. 11
使用列表外部變數foo改變了
Python程式碼
1. >>> def inc(x): x[0] = x[0] + 1
2.
3. >>> foo = [10]
4. >>> inc(foo)
5. >>> foo
6. [11]
3. 關鍵字引數和預設值
位置:是指根據引數的對應位置傳參,如def a(a,b,c):,呼叫a(1,2,3),1傳給a,2傳給b,3傳給c,這樣引數位置容易記混。
關鍵字引數,適用於大規模程式,清晰
Python程式碼
1. >>> def hello(name, greeting):
2. print '%s, %s!' %(greeting, name)
3.
4.
5. >>> hello('sun', 'Hello') #位置引數
6. Hello, sun!
7. >>> hello(name = 'Sun', greeting = 'Hello') #關鍵字引數
8. Hello, Sun!
9. >>> hello(greeting = 'Hello', name = 'Sun') #關鍵字引數,不必關心位置
10. Hello, Sun!
預設值
Python程式碼
1. >>> def hello(name = 'world', greeting = 'Hello'):
2. print '%s, %s!' %(greeting, name)
3.
4.
5. >>> hello()
6. Hello, world!
7. >>> hello('Sun')
8. Hello, Sun!
9. >>> hello(greeting = 'Hi')
10. Hi, world!
位置引數與關鍵字引數混用,將位置引數放在前面。儘量避免這麼用,容易引起混亂。
Python程式碼
1. >>> def hello(name, greeting = 'Hello', punc = '!'):
2. print '%s, %s%s' %(greeting, name, punc)
3.
4.
5. >>> hello('Sun')
6. Hello, Sun!
7. >>> hello('Sun', 'Hi')
8. Hi, Sun!
9. >>> hello('Sun', punc = '..')
10. Hello, Sun..
11. >>> hello() #因為name是必須要有,若有預設值,則可沒有
12.
13. Traceback (most recent call last):
14. File "<pyshell#385>", line 1, in <module>
15. hello()
16. TypeError: hello() takes at least 1 argument (0 given)
17. >>>
4. 收集引數——在定義時使用*或**,用來收集引數,允許使用不定數量的引數
*:收集其餘的位置引數並作為元組 返回
Python程式碼
1. >>> def print_params2(title, *params):
2. print title
3. print params
4.
5.
6. >>> print_params2('Param:', 1, 2, 3)
7. Param:
8. (1, 2, 3) #以元組形式返回
9. >>> print_params2('Param:') #不提供收集元素時,返回空元組
10. Param:
11. ()
12. >>> print_params2('Param:', 1)
13. Param:
14. (1,) #只有一個元素時,仍為元組
**:收集其餘的關鍵字引數並作為字典 返回,可與其他混用
Python程式碼
1. >>> def print_params3(x, y, z = 3, *pospar, **keypar):
2. print x, y, z
3. print pospar
4. print keypar
5.
6.
7. >>> print_params3(1,2,3,4,5,6,7, fool = 1, bar = 2)
8. 1 2 3
9. (4, 5, 6, 7)
10. {'fool': 1, 'bar': 2}
11. >>> print_params3(1,2)
12. 1 2 3
13. ()
14. {}
5. 收集引數的翻轉過程——在呼叫時使用*或**,將引數分配到定義的引數中,用於字典或列表分割時
用於列表
Python程式碼
1. >>> def add(x,y): return x + y
2.
3. >>> params = (1,2)
4. >>> add(*params)
5. 3
用於字典
Python程式碼
1. >>> def hello(name, greeting):
2. print '%s, %s!' %(greeting, name)
3.
4.
5. >>> params = {'name': 'Sir Robin', 'greeting': 'Welcome'}
6. >>> hello(**params)
7. Welcome, Sir Robin!
8.
9.
引數使用例項
Python程式碼
1. #模擬步長大於0的range()
2. >>> interval(10)
3. start = 0 stop = 10 step = 1
4. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5. >>> def interval(start, stop=None, step=1):
6. 'Imitates range() for step > 0'
7. if stop is None: #若未給stop指定值
8. start, stop = 0, start #多個賦值,0賦值給start,start的值賦值給stop
9. result = []
10. i = start
11. while i < stop:
12. result.append(i)
13. i += step
14. return result
15.
16. #
17. >>> def story(**kwds):
18. return '%(job)s called %(name)s.' %kwds
19.
20. >>> def power(x, y ,*others):
21. if others:
22. print 'Received redundant parameters:',others
23. return pow(x,y)
24.
25. #使用
26. >>> params = {'job':'language','name':'python'}
27. >>> print story(**params) #呼叫時分割字典,定義中收集
28. language called python.
29. >>> del params['job']
30. >>> print story(job='test', **params)
31.
32. >>> power(2,3,'test')
33. Received redundant parameters: test
34.
35. >>> params = (5,) * 2 #即(5,5)
36. >>> power(*params) #先分割,在賦給x,y
37. 3125
6.作用域
x = 1, 將名字x引用到值1上,類似字典
內建函式vars()返回這個字典
Python程式碼
1. >>> x = 1
2. >>> scope = vars()
3. >>> scope['x']
4. 1
5. >>> scope['x'] += 1 #一般情況下,vars()返回的字典不能修改
6. >>> x
7. 2
區域性變數,全域性變數
函式內部訪問全域性變數,慎用!
Python程式碼
1. >>> def com(para): print para + external
2.
3. >>> external = 'external'
4. >>> com('param ')
5. param external
若全域性變數與區域性變數名字相同,會被區域性變數覆蓋,可使用global()類似vars(),獲得全域性變數的字典
Python程式碼
1. >>> def com(para): print para + globals()['para']
2.
3. >>> para = 'berry'
4. >>> com('test ')
5. test berry
重繫結全域性變數,將變數引用到其他新值——函式內部宣告全域性變數
Python程式碼
1. >>> x = 1
2. >>> def change_global():
3. global x
4. x = x + 1
5.
6.
7. >>> change_global()
8. >>> x
9. 2
巢狀作用域——函式中定義函式,例如閉包
外部作用域中的變數一般不能被改變,但是用閉包,每次呼叫外層函式,內部函式都會被重新繫結,也即外部作用域factor每次都有一個新值
Python程式碼
1. >>> def multiplier(factor):
2. def multiplyByFactor(number):
3. return number * factor
4. return multiplyByFactor #返回一個函式,這時並未呼叫
5.
6. >>> double = multiplier(2) #double是一個函式
7. >>> double #double是一個函式
8. <function multiplyByFactor at 0x0214C6F0>
9. >>> double(5) #呼叫multiplyByFactor(number)
10. 10
11. >>> multiplier(2)(5) #效果同上
12. 10
7. 遞迴——每呼叫一個函式,都會建立一個新的名稱空間,意味著當函式呼叫自身時,實際上呼叫的是兩個不同的函式
階乘
Python程式碼
1. >>> def factorial(n):
2. if n == 1:
3. return 1
4. else:
5. return n * factorial(n-1)
6.
7. >>> factorial(5)
8. 120
冪
Python程式碼
1. >>> def power(x, n):
2. if n == 0:
3. return 1
4. else:
5. return x * power(x, n - 1)
6.
7.
8. >>> power(2,3)
9. 8
遞迴例項——二元搜尋
前提:排好序
若上下限相同,則那就是數字所在位置,返回;
否則,找到兩者的中間,查詢數字是在左側還是右側,繼續查詢數字所在的那半部分。
Python程式碼
1. >>> def search(sequence, number, lower = 0, upper = None):
2. if upper is None: upper = len(sequence) - 1
3. if lower == upper:
4. assert number == sequence[upper]
5. return upper
6. else:
7. middle = (lower + upper) // 2
8. if number > sequence[middle]:
9. return search(sequence, number, middle + 1, upper)
10. else:
11. return search(sequence, number, lower, middle)
12.
13.
14. >>> seq = [34, 67, 8, 123, 4, 100, 95]
15. >>> seq.sort()
16. >>> seq
17. [4, 8, 34, 67, 95, 100, 123]
18. >>> search(seq, 34)
19. 2
總結:
元組輸出格式化,直接使用鍵,而不需要加引號
Python程式碼
1. >>> d = {'a':1, 'b':2}
2. >>> print '%(a)s corresponds to %(b)s.' %d #注意a有括號,無引號
3. 1 corresponds to 2.