1. 程式人生 > >python基礎06

python基礎06

所有 set __next__ oca 修改 源代碼 可維護 簡單的 func

Python基礎學習06


實現裝飾器知識儲備

裝飾器

生成器

叠代器

目錄結構


一、實現裝飾器知識儲備

1、函數即“變量

技術分享圖片
 1 x = 1
 2 y = 2
 3 print(x,y)
 4 
 5 y = 2
 6 x = 1
 7 print(x,y)
 8 
 9 def bar():
10     print("in the bar")
11 def foo():
12     print("in the foo")
13     bar()
14 foo()
15 
16 def foo():
17     print("in the foo")
18     bar()
19
def bar(): 20 print("in the bar") 21 foo()
View Code

函數調用順序:其他高級語言類似,Python 不允許在函數未聲明之前,對其進行引用或者調用

python為解釋執行,函數foo在調用前已經聲明了bar和foo,所以bar和foo無順序之分(和變量的調用相同)

2、高階函數

滿足下列條件之一就可成函數為高階函數:

a:把一個函數名當做實參傳給另外一個函數(在不修改被裝飾函數源代碼的情況下為其添加功能)

技術分享圖片
 1 #把一個函數名當做實參傳給另外一個函數
 2 def bar():
 3     print("in the bar")
4 def test_1 (func): 5 print(func) 6 test_1(bar) #輸出:<function bar at 0x00000264BDEED488> 即bar()函數的內存地址 7 8 def bar(): 9 print("in the bar") 10 def test_1 (func): 11 print(func) #<function bar at 0x00000264BDEED488> 12 func() #in the bar (bar()函數的運行結果) 13 test_1(bar)
14 15 #應用:統計函數運行時間 16 import time 17 def bar(): 18 print("in the bar") 19 time.sleep(1) 20 def test_1 (func): 21 start_time = time.time() 22 func() #in the bar (bar()函數的運行結果) 23 stop_time = time.time() 24 print("the func run time:%s "%(stop_time - start_time)) 25 test_1(bar)
View Code

b:返回值中包含函數名(不修改函數的調用方式)

技術分享圖片
 1 import time
 2 def bar():
 3     print("in the bar")
 4     time.sleep(1)
 5 def test_2(func):
 6     print(func)
 7     return func
 8 t = test_2(bar)   #獲取bar()函數的內存地址,把其當做返回值傳給變量t
 9 t()        #運行函數
10 
11 def bar():
12     print("in the bar")
13     time.sleep(1)
14 def test_2(func):
15     print(func)
16     return func
17 bar = test_2(bar)   #獲取bar()函數的內存地址,把其當做返回值傳給變量bar
18 bar()        #不修改函數的調用方式
View Code

3、嵌套函數

定義:在一個函數體內創建另外一個函數;即在一個函數的函數體內用def去聲明一個新的函數(而不是去調用)

技術分享圖片
1 #最簡單的嵌套函數
2 def foo():
3     print("in the foo")
4     def bar():         #相當於局部變量
5         print("in the bar")
6     bar()            #調用bar()函數
7 foo()      #調用函數
View Code

局部作用域和全局作用域的訪問順序:

技術分享圖片
 1 x=0
 2 def grandpa():
 3     # x=1
 4     def dad():
 5         x=2
 6         def son():
 7             x=3
 8             print(x)
 9         son()
10     dad()
11 grandpa()
View Code

二、裝飾器

定義:本質是函數(裝飾其他函數)就是為其他函數添加附加功能
原則
a、不能修改被裝飾的函數的源代碼
b、不能修改被裝飾的函數的調用方式
即裝飾器對其裝飾的函數是完全透明的

高階函數+嵌套函數=》裝飾器

簡單裝飾器:(不能傳參)

技術分享圖片
 1 #統計運行時間
 2 import time
 3 def timer(func):
 4     def deco ():
 5         start_time = time.time()
 6         func()        
 7         stop_time = time.time()
 8         print("the func run time:%s "%(stop_time - start_time))
 9     return deco
10 def test_1 ():
11     time.sleep(1)
12     print("in the test_1")
13 def test_2 ():
14     time.sleep(2)
15     print("in the test_2")
16 
17 test_1 = timer(test_1)    #即:@timer
18 test_2 = timer(test_2)    #即:@timer
19 test_1()     #沒有修改被裝飾的函數的源代碼,也沒有修改被裝飾的函數的調用方式,並且添加了新功能
20 test_2()     #沒有修改被裝飾的函數的源代碼,也沒有修改被裝飾的函數的調用方式,並且添加了新功能
21 
22 #正規寫法
23 import time
24 def timer(func):
25     def deco ():
26         start_time = time.time()
27         func()       
28         stop_time = time.time()
29         print("the func run time:%s "%(stop_time - start_time))
30     return deco
31 @timer      #即:test_1 = timer(test_1)
32 def test_1 ():
33     time.sleep(1)
34     print("in the test_1")
35 @timer      #即:test_2 = timer(test_2)
36 def test_2 ():
37     time.sleep(2)
38     print("in the test_2")
39 
40 test_1()     #沒有修改被裝飾的函數的源代碼,也沒有修改被裝飾的函數的調用方式,並且添加了新功能
41 test_2()     #沒有修改被裝飾的函數的源代碼,也沒有修改被裝飾的函數的調用方式,並且添加了新功能
View Code

加入參數:

技術分享圖片
 1 import time
 2 def timer(func):
 3     def deco (*args,**kwargs):      #deco() 即 test_1、test_2 在此傳入參數
 4         start_time = time.time()
 5         func(*args,**kwargs)
 6         stop_time = time.time()
 7         print("the func run time:%s "%(stop_time - start_time))
 8     return deco
 9 @timer      #即:test_1 = timer(test_1)
10 def test_1 ():
11     time.sleep(1)
12     print("in the test_1")
13 @timer      #即:test_2 = timer(test_2)
14 def test_2 (name):
15     time.sleep(0.5)
16     print("test_2",name)
17 
18 test_1()
19 test_2("zz")
View Code

真正的裝飾器:

技術分享圖片
 1 #在原來函數上添加認證登錄功能,並且允許用戶選擇多種方式進行登錄
 2 import time
 3 user,passwd = zz,123
 4 def auth(auth_type):
 5     def out_wrapper(func):
 6         def wrapper(*args,**kwargs):
 7             if auth_type == "local":
 8                 username = input("username:").strip()
 9                 password = input("password:").strip()
10                 if username == user and password ==passwd:
11                     print("login!!")
12                     res = func(*args,**kwargs)
13                     return res                #之前的裝飾器沒有返回值,如果有返回值則需要定義返回值
14                 else:
15                     exit("認證失敗")
16             elif auth_type == "ldap":
17                 print("還不會ldap!")
18         return wrapper
19     return out_wrapper
20 
21 def index():
22     print("welcome to index page!")
23 @auth(auth_type = "local")  #home = auth()
24 def home():
25     print("welcome to home page!")
26     return "from home"
27 @auth(auth_type = "ldap")
28 def bbs():
29     print("welcome to bbs page!")
30 
31 index()
32 print(home())  #有返回值的調用
33 bbs()
View Code

三、生成器

1、列表生成式

技術分享圖片
1 a = [1,2,3]       #正常定義列表(數據是寫死的)
2 b = [i*2 for i in range(5)]   #列表生成式
3 print(b)     #[0, 2, 4, 6, 8]
4 
5 b = []      #與上面效果一樣但是代碼量大
6 for i in range(5):
7     b.append(i*2)
8 print(b)
View Code

2、生成器

在Python中一邊循環一邊計算的機制,稱為生成器:generator。

要創建一個generator,有很多種方法。

第一種方法:只要把一個列表生成式的[]改成(),就創建了一個generator:

技術分享圖片
1 L = [x * x for x in range(10)]
2 g = (x * x for x in range(10))
3 print(g)   #<generator object <genexpr> at 0x0000022A5A330AF0>
View Code

創建L和g的區別僅在於最外層的[]和(),L是一個list,而g是一個generator

我們可以直接打印出list的每一個元素,但我們需要通過next()函數獲得generator的下一個返回值:

技術分享圖片
1 L = [x * x for x in range(10)]
2 g = (x * x for x in range(10))
3 print(g)   #<generator object <genexpr> at 0x0000022A5A330AF0>
4 
5 print(g.__next__())  # 0
6 print(g.__next__())  # 1
7 print(g.__next__())  # 4
8 print(g.__next__())  # 9
9 print(g.__next__())  # 16
View Code

generator也是可叠代對象,可以用for循環:

技術分享圖片
 1 g = (x * x for x in range(10))
 2 print(g)   #<generator object <genexpr> at 0x0000022A5A330AF0>
 3 for i in g :
 4     print(i)
 5 print(g.__next__())  
 6 ‘‘‘
 7 Traceback (most recent call last):
 8     print(g.__next__())  # 0
 9 StopIteration
10 ‘‘‘
View Code


generator保存的是算法,每次調用__next__(g),就計算出g的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤

另一種方法:如果一個函數定義中包含yield關鍵字,那麽這個函數就不再是一個普通函數,而是一個generator:

技術分享圖片
 1 #用函數生成斐波那契數列
 2 def fib(max):
 3     n, a, b = 0, 0, 1
 4     while n < max:
 5         print(b)
 6         a, b = b, a + b
 7         ‘‘‘a, b = b, a + b相當於:
 8         t = (b, a + b) # t是一個tuple
 9         a = t[0]
10         b = t[1]
11         ‘‘‘
12         n = n + 1
13     return done
14 fib(10)
15 
16 #print(b)改為yield b 就變成了生成器
17 def fib(max):
18     n, a, b = 0, 0, 1
19     while n < max:
20         yield b
21         a, b = b, a + b
22         n = n + 1
23     return done
24 print(fib(10))  #<generator object fib at 0x000002371FC30AF0>
25 f = fib(10)
26 print(f.__next__())  # 1
27 print(f.__next__())  # 1
28 print(f.__next__())  # 2
29 print("_____")      #_____
30 print(f.__next__())  # 3
View Code


同樣的,把函數改成generator後,我們基本上從來不會用next()來獲取下一個返回值,而是直接使用for循環來叠代,但是用for循環調用generator時,發現拿不到generator的return語句的返回值,計算到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤,如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:

技術分享圖片
 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         yield b
 5         a, b = b, a + b
 6         n = n + 1
 7     return done
 8 g = fib(5)
 9 while True:
10     try:
11         x = next(g)
12         print(g:, x)
13     except StopIteration as e:
14         print(Generator return value:, e.value)
15         break
16 ‘‘‘
17 g: 1
18 g: 1
19 g: 2
20 g: 3
21 g: 5
22 Generator return value: done
23 ‘‘‘
View Code

特性:a、生成器只有在調用時才會生成數據。

b、只記錄當前位置。

c、只有一個__next__()方法。

四、叠代器

可以直接作用於for循環的對象統稱為可叠代對象:Iterable

可以使用isinstance()判斷一個對象是否是Iterable對象:

技術分享圖片
1 from collections import Iterable
2 print(isinstance([], Iterable))  #True
3 print(isinstance({}, Iterable))  #True
4 print(isinstance(abc, Iterable))  #True
5 print(isinstance((x for x in range(10)), Iterable))  #True
6 print(isinstance(100, Iterable))  #False
View Code

可以被next()函數調用並不斷返回下一個值的對象稱為叠代器:Iterator

可以使用isinstance()判斷一個對象是否是Iterator對象:

技術分享圖片
1 from collections import Iterator
2 print(isinstance((x for x in range(10)), Iterator))  #True
3 print(isinstance([], Iterator))  #False
4 print(isinstance({}, Iterator))  #False
5 print(isinstance(abc, Iterator))  #False
View Code

生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator,把list、dict、str等Iterable變成Iterator可以使用iter()函數:

技術分享圖片
1 isinstance(iter([]), Iterator)  #True
2 isinstance(iter(abc), Iterator)  #True
View Code

Python的for循環本質上就是通過不斷調用next()函數實現的:

技術分享圖片
 1 for x in [1, 2, 3, 4, 5]:
 2     pass
 3 #等價於:
 4 # 首先獲得Iterator對象:
 5 it = iter([1, 2, 3, 4, 5])
 6 # 循環:
 7 while True:
 8     try:
 9         # 獲得下一個值:
10         x = next(it)
11     except StopIteration:
12         # 遇到StopIteration就退出循環
13         break
View Code

五、目錄結構

規範的目錄結構能更好的控制程序結構,讓程序具有更高的可讀性。
"項目目錄結構"其實也是屬於"可讀性和可維護性"的範疇,設計一個層次清晰的目錄結構,就是為了達到以下兩點:
1.可讀性高: 不熟悉這個項目的代碼的人,一眼就能看懂目錄結構,知道程序啟動腳本是哪個,測試目錄在哪兒,配置文件在哪兒等等。從而非常快速的了解這個項目。
2.可維護性高: 定義好組織規則後,維護者就能很明確地知道,新增的哪個文件和代碼應該放在什麽目錄之下。這個好處是,隨著時間的推移,代碼/配置的規模增加,項目結構不會混亂,仍然能夠組織良好。

常用目錄樹:

項目名/
|-- bin/
|   |-- 可執行文件
|
|-- 項目/
|   |-- tests/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |
|   |-- __init__.py
|   |-- main.py
|
|-- docs/
|   |-- conf.py
|   |-- abc.rst
|
|-- setup.py
|-- requirements.txt
|-- README

1.bin/: 存放項目的一些可執行文件。
2.項目/: 存放項目的所有源代碼。(1) 源代碼中的所有模塊、包都應該放在此目錄。不要置於頂層目錄。(2) 其子目錄tests/存放單元測試代碼; (3) 程序的入口最好命名為main.py。
3.docs/: 存放一些文檔。
4.setup.py: 安裝、部署、打包的腳本。
5.requirements.txt: 存放軟件依賴的外部Python包列表。
6.README: 項目說明文件。

README需要說明以下幾個事項:
1.軟件定位,軟件的基本功能。
2.運行代碼的方法: 安裝環境、啟動命令等。
3.簡要的使用說明。
4.代碼目錄結構說明,更詳細點可以說明軟件的基本原理。
5.常見問題說明。

python基礎06