Python學習第四天
一、裝飾器
函數調用順序:
其他高級語言類似,Python 不允許在函數未聲明之前,對其進行引用或者調用
高階函數:
滿足下列條件之一就可成函數為高階函數
-
某一函數當做參數傳入另一個函數中
-
函數的返回值包含n個函數,n>0
內嵌函數和變量作用域:
定義:在一個函數體內創建另外一個函數,這種函數就叫內嵌函數(基于python支持靜態嵌套域)
閉包:
如果在一個內部函數里,對在外部作用域(但不是在全局作用域)的變量進行引用,那么內部函數就被認為是 closure
內嵌函數+高階函數+閉包=》裝飾器
范例一:函數參數固定
1 def decorartor(func): 2 def wrapper(n): 3 print 'starting' 4 func(n) 5 print 'stopping' 6 return wrapper 7 8 9 def test(n): 10 print 'in the test arg is %s' %n 11 12 decorartor(test)('alex')
范例二:函數參數不固定
1 def decorartor(func): 2 def wrapper(*args,**kwargs): 3 print 'starting' 4 func(*args,**kwargs) 5 print 'stopping' 6 return wrapper 7 8 9 def test(n,x=1): 10 print 'in the test arg is %s' %n 11 12 decorartor(test)('alex',x=2)
范例三:無參裝飾器
1 import time 2 def decorator(func): 3 def wrapper(*args,**kwargs): 4 start=time.time() 5 func(*args,**kwargs) 6 stop=time.time() 7 print 'run time is %s ' %(stop-start) 8 print timeout 9 return wrapper 10 11 @decorator 12 def test(list_test): 13 for i in list_test: 14 time.sleep(0.1) 15 print '-'*20,i 16 17 18 #decorator(test)(range(10)) 19 test(range(10))
范例四:有參裝飾器
1 import time 2 def timer(timeout=0): 3 def decorator(func): 4 def wrapper(*args,**kwargs): 5 start=time.time() 6 func(*args,**kwargs) 7 stop=time.time() 8 print 'run time is %s ' %(stop-start) 9 print timeout 10 return wrapper 11 return decorator 12 @timer(2) 13 def test(list_test): 14 for i in list_test: 15 time.sleep(0.1) 16 print '-'*20,i 17 18 #timer(timeout=10)(test)(range(10)) 19 test(range(10))
二、生成器
1、列表生成式
1 >>> a = [i+1 for i in range(10)] 2 >>> a 3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2、生成器
通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。
所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。
要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:
1 >>> L = [x * x for x in range(10)] 2 >>> L 3 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 4 >>> g = (x * x for x in range(10)) 5 >>> g 6 <generator object <genexpr> at 0x1022ef630>
斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print(b) 5 a, b = b, a + b 6 n = n + 1 7 return 'done'
仔細觀察,可以看出,fib函數實際上是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出后續任意的元素,這種邏輯其實非常類似generator。
也就是說,上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:
1 def fib(max): 2 n,a,b = 0,0,1 3 4 while n < max: 5 #print(b) 6 yield b 7 a,b = b,a+b 8 9 n += 1 10 11 return 'done'
這就是定義generator的另一種方法。如果一個函數定義中包含yield關鍵字,那么這個函數就不再是一個普通函數,而是一個generator:
1 >>> f = fib(6) 2 >>> f 3 <generator object fib at 0x104feaaa0>
這里,最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。
1 data = fib(10) 2 print(data) 3 4 print(data.__next__()) 5 print(data.__next__()) 6 print("干點別的事") 7 print(data.__next__()) 8 print(data.__next__()) 9 print(data.__next__()) 10 print(data.__next__()) 11 print(data.__next__()) 12 13 #輸出 14 <generator object fib at 0x101be02b0> 15 1 16 干點別的事 17 3 18 8
但是用for循環調用generator時,發現拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:
1 >>> g = fib(6) 2 >>> while True: 3 ... try: 4 ... x = next(g) 5 ... print('g:', x) 6 ... except StopIteration as e: 7 ... print('Generator return value:', e.value) 8 ... break 9 ... 10 g: 1 11 g: 1 12 g: 2 13 g: 3 14 g: 5 15 g: 8 16 Generator return value: done
三、迭代器
我們已經知道,可以直接作用于for循環的數據類型有以下幾種:
一類是集合數據類型,如list、tuple、dict、set、str等;
一類是generator,包括生成器和帶yield的generator function。
這些可以直接作用于for循環的對象統稱為可迭代對象:Iterable。
可以使用isinstance()判斷一個對象是否是Iterable對象
1 >>> from collections import Iterable 2 >>> isinstance([], Iterable) 3 True 4 >>> isinstance({}, Iterable) 5 True 6 >>> isinstance('abc', Iterable) 7 True 8 >>> isinstance((x for x in range(10)), Iterable) 9 True 10 >>> isinstance(100, Iterable) 11 False
*可以被next()函數調用并不斷返回下一個值的對象稱為迭代器:Iterator。
1 可以使用isinstance()判斷一個對象是否是Iterator對象: 2 3 >>> from collections import Iterator 4 >>> isinstance((x for x in range(10)), Iterator) 5 True 6 >>> isinstance([], Iterator) 7 False 8 >>> isinstance({}, Iterator) 9 False 10 >>> isinstance('abc', Iterator) 11 False
生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數:
1 >>> isinstance(iter([]), Iterator) 2 True 3 >>> isinstance(iter('abc'), Iterator) 4 True
你可能會問,為什么list、dict、str等數據類型不是Iterator?
這是因為Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用并不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它才會計算。
Iterator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。
小結
凡是可作用于for循環的對象都是Iterable類型;
凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
Python的for循環本質上就是通過不斷調用next()函數實現的,例如:
for x in [1, 2, 3, 4, 5]:
pass
實際上完全等價于:
1 # 首先獲得Iterator對象: 2 it = iter([1, 2, 3, 4, 5]) 3 # 循環: 4 while True: 5 try: 6 # 獲得下一個值: 7 x = next(it) 8 except StopIteration: 9 # 遇到StopIteration就退出循環 10 break
四、json&pickle序列化
用于序列化的兩個模塊
- json,用于字符串 和 python數據類型間進行轉換
- pickle,用于python特有的類型 和 python的數據類型間進行轉換
Json模塊提供了四個功能:dumps、dump、loads、load
pickle模塊提供了四個功能:dumps、dump、loads、load

五、目錄開發規范
假設你的項目名為foo:
Foo/
|-- bin/
| |-- foo
|
|-- foo/
| |-- tests/
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py
|
|-- docs/
| |-- conf.py
| |-- abc.rst
|
|-- setup.py
|-- requirements.txt
|-- README
簡要解釋一下:
bin/: 存放項目的一些可執行文件,當然你可以起名script/之類的也行。foo/: 存放項目的所有源代碼。(1) 源代碼中的所有模塊、包都應該放在此目錄。不要置于頂層目錄。(2) 其子目錄tests/存放單元測試代碼; (3) 程序的入口最好命名為main.py。docs/: 存放一些文檔。setup.py: 安裝、部署、打包的腳本。requirements.txt: 存放軟件依賴的外部Python包列表。README: 項目說明文件。
關于README的內容
目的是能簡要描述該項目的信息,讓讀者快速了解這個項目。
它需要說明以下幾個事項:
- 軟件定位,軟件的基本功能。
- 運行代碼的方法: 安裝環境、啟動命令等。
- 簡要的使用說明。
- 代碼目錄結構說明,更詳細點可以說明軟件的基本原理。
- 常見問題說明。
浙公網安備 33010602011771號