Python自動化 【第四篇】:Python基礎-裝飾器 生成器 迭代器 Json & pickle
目錄:
- 裝飾器
- 生成器
- 迭代器
- Json & pickle 數據序列化
- 軟件目錄結構規范
1. Python裝飾器
裝飾器:本質是函數,(功能是裝飾其它函數)就是為其他函數添加附加功能
原則:
>不能修改被裝飾的函數的源代碼
>不能修改被裝飾的函數的調用方式
實現裝飾器知識儲備:
>函數即“變量”
>高階函數
a)把一個函數名當做實參傳給另外一個函數(在不修改源代碼的請情況下)
b)返回值中包含函數名(不修改函數的調用方式)
>嵌套函數
高階函數+嵌套函數=》裝飾器
1.1 函數的調用順序:
Python不允許函數在未聲明之前對其進行引用或者調用
錯誤案例一:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def foo(): print 'in the foo' bar() foo()
錯誤案例二:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def foo(): print 'foo' bar() foo() def bar(): print 'bar'
以上兩個案例都會報錯:NameError: global
name 'bar'
is
not
defined
正確案例一:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def bar(): print 'in the bar' def foo(): print 'in the foo' bar() foo()
正確案例二:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def foo(): print 'in the foo' bar() def bar(): print 'in the bar' foo()
(python為解釋執行,函數foo在調用前已經聲明了bar和foo,所以bar和foo無順序之分)
1.2 高階函數
高階函數需滿足以下兩個條件:
a)某一函數當做參數傳入另一函數中(在不修改源代碼的請情況下)
b)返回值中包含函數名(不修改函數的調用方式)
高階函數示例:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def bar(): print("In the bar") def foo(func): res = func() return res foo(bar)
高階函數進階:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def foo(func): return func print 'Function body is %s' %(foo(bar)) print 'Function name is %s' %(foo(bar).func_name) foo(bar)() #foo(bar)() 等同于bar=foo(bar)然后bar() bar=foo(bar) bar()
1.3 內嵌函數和變量作用域
定義:在一個函數體內創建另外一個函數,這種函數就叫內嵌函數(基于python支持靜態嵌套域)
函數嵌套示范:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def foo(): def bar(): print 'in the bar' bar() foo()
局部作用域和全局作用域的訪問順序
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
x=0 def grandpa(): # x=1 def dad(): x=2 def son(): x=3 print x son() dad() grandpa()
范例一:函數參數固定
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def decorartor(func): def wrapper(n): print 'starting' func(n) print 'stopping' return wrapper def test(n): print 'in the test arg is %s' % n decorartor(test)('Tom')
范例二:函數參數不固定
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def decorartor(func): def wrapper(*args, **kwargs): print 'starting' func(*args, **kwargs) print 'stopping' return wrapper def test(n, x=1): print 'in the test arg is %s' % n decorartor(test)('alex', x=2)
無參裝飾器
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
import time def decorator(func): def wrapper(*args, **kwargs): start = time.time() func(*args, **kwargs) stop = time.time() print 'run time is %s ' % (stop - start) print timeout return wrapper @decorator def test(list_test): for i in list_test: time.sleep(0.1) print '-' * 20, i # decorator(test)(range(10)) test(range(10))
有參裝飾器
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
import time def timer(timeout=0): def decorator(func): def wrapper(*args, **kwargs): start = time.time() func(*args, **kwargs) stop = time.time() print 'run time is %s ' % (stop - start) print timeout return wrapper return decorator @timer(2) def test(list_test): for i in list_test: time.sleep(0.1) print '-' * 20, i # timer(timeout=10)(test)(range(10)) test(range(10))
2. 生成器
a = [i*2 for I in range(10)] #列表生成式
一邊循環一邊計算的機制,稱為生成器:generator
要創建一個generator,有很多種方法。
第一種方法很簡單,只要把一個列表生成式的[]
改成()
,就創建了一個generator:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630>
創建L
和g
的區別僅在于最外層的[]
和()
,L
是一個list,而g
是一個generator。
如果要一個一個打印出來,可以通過next()
函數獲得generator的下一個返回值:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> next(g) 0 >>> next(g) 1 >>> next(g) 4 >>> next(g) 9 >>> next(g) 16 >>> next(g) 25 >>> next(g) 36 >>> next(g) 49 >>> next(g) 64 >>> next(g) 81 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
generator保存的是算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤。
當然,上面這種不斷調用next(g)實在是太變態了,正確的方法是使用for循環,因為generator也是可迭代對象
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> g = (x * x for x in range(10)) >>> for n in g: ... print(n) ... 0 1 4 9 16 25 36 49 64 81
所以,我們創建了一個generator后,基本上永遠不會調用next(),而是通過for循環來迭代它,并且不需要關心StopIteration的錯誤。
generator非常強大。如果推算的算法比較復雜,用類似列表生成式的for循環無法實現的時候,還可以用函數來實現
斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 return 'done'
上面的函數可以輸出斐波那契數列的前N個數:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> fib(10) 1 1 2 3 5 8 13 21 34 55 done
仔細觀察,可以看出,fib函數實際上是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出后續任意的元素,這種邏輯其實非常類似generator。
也就是說,上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
def fib(max): n,a,b = 0,0,1 while n < max: #print(b) yield b a,b = b,a+b n += 1 return 'done'
這就是定義generator的另一種方法。如果一個函數定義中包含yield
關鍵字,那么這個函數就不再是一個普通函數,而是一個generator:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> f = fib(6) >>> f <generator object fib at 0x104feaaa0>
這里,最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return
語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()
的時候執行,遇到yield
語句返回,再次執行時從上次返回的yield
語句處繼續執行。
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
data = fib(10) print(data) print(data.__next__()) print(data.__next__()) print("干點別的事") print(data.__next__()) print(data.__next__()) print(data.__next__()) print(data.__next__()) print(data.__next__())
#輸出
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
<generator object fib at 0x101be02b0> 1 1 干點別的事 2 3 5 8 13
在上面fib的例子,我們在循環過程中不斷調用yield,就會不斷中斷。當然要給循環設置一個條件來退出循環,不然就會產生一個無限數列出來。
同樣的,把函數改成generator后,我們基本上從來不會用next()來獲取下一個返回值,而是直接使用for循環來迭代:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> for n in fib(6): ... print(n) ... 1 1 2 3 5 8
但用for
循環調用generator時,發現拿不到generator的return
語句的返回值。如果想要拿到返回值,必須捕獲StopIteration
錯誤,返回值包含在StopIteration
的value
中:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> g = fib(6) >>> while True: ... try: ... x = next(g) ... print('g:', x) ... except StopIteration as e: ... print('Generator return value:', e.value) ... break ... g: 1 g: 1 g: 2 g: 3 g: 5 g: 8 Generator return value: done
還可通過yield實現在單線程的情況下實現并發運算的效果
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
import time def consumer(name): print("%s 準備吃包子啦!" %name) while True: baozi = yield print("包子[%s]來了,被[%s]吃了!" %(baozi,name)) def producer(name): c = consumer('A') c2 = consumer('B') c.__next__() c2.__next__() print("老子開始準備做包子啦!") for i in range(10): time.sleep(1) print("做了2個包子!") c.send(i) c2.send(i) producer("Tom")
3. 迭代器
我們已經知道,可以直接作用于for循環的數據類型有以下幾種:
一類是集合數據類型,如list、tuple、dict、set、str等;
一類是generator,包括生成器和帶yield的generator function。
這些可以直接作用于for循環的對象統稱為可迭代對象:Iterable。
可以使用isinstance()判斷一個對象是否是Iterable對象:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> from collections import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance('abc', Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False
而生成器不但可以作用于for
循環,還可以被next()
函數不斷調用并返回下一個值,直到最后拋出StopIteration
錯誤表示無法繼續返回下一個值了。
*可以被next()
函數調用并不斷返回下一個值的對象稱為迭代器:Iterator
可以使用isinstance()判斷一個對象是否是Iterator對象:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 >>> from collections import Iterator 2 3 >>> isinstance((x for x in range(10)), Iterator) 4 5 True 6 7 >>> isinstance([], Iterator) 8 9 False 10 11 >>> isinstance({}, Iterator) 12 13 False 14 15 >>> isinstance('abc', Iterator) 16 17 False
生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
>>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True
Python的Iterator
對象表示的是一個數據流,Iterator對象可以被next()
函數調用并不斷返回下一個數據,直到沒有數據時拋出StopIteration
錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()
函數實現按需計算下一個數據,所以Iterator
的計算是惰性的,只有在需要返回下一個數據時它才會計算。
小結:
凡是可作用于for循環的對象都是Iterable類型;
凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
Python的for循環本質上就是通過不斷調用next()函數實現的,例如
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
for x in [1, 2, 3, 4, 5]: pass
實際上完全等價于:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
# 首先獲得Iterator對象: it = iter([1, 2, 3, 4, 5]) # 循環: while True: try: # 獲得下一個值: x = next(it) except StopIteration: # 遇到StopIteration就退出循環 break
4. json和pickle
用于序列化的兩個模塊
json,用于字符串和python數據類型間進行轉換
pickle,用于python特有的類型和python的數據類型間進行轉換
Json模塊提供了四個功能:dumps、dump、loads、load
pickle模塊提供了四個功能:dumps、dump、loads、load
5. 軟件目錄結構規范
設計軟件目錄結構為了達到以下兩點:
>可讀性高: 不熟悉這個項目的代碼的人,一眼就能看懂目錄結構,知道程序啟動腳本是哪個,測試目錄在哪兒,配置文件在哪兒等等。從而非常快速的了解這個項目。
>可維護性高: 定義好組織規則后,維護者就能很明確地知道,新增的哪個文件和代碼應該放在什么目錄之下。這個好處是,隨著時間的推移,代碼/配置的規模增加,項目結構不會混亂,仍然能夠組織良好。
目錄組織方式:
假設你的項目名為foo, 我比較建議的最方便快捷目錄結構這樣就足夠了:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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: 項目說明文件。
文章列表