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