Python裝飾器、生成器、內置函數、Json-Day05

 裝飾器

裝飾器本質上就是一個python函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外功能,裝飾器的返回值也是一個函數對象。它常常用於有切面需求的場景,好比:插入日誌、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,咱們就能夠抽離大量與函數功能自己無關的雷同代碼並繼續重用。歸納講,裝飾器的做用就是爲依據存在的對象添加額外的功能。
html

定義:本質是函數,(裝飾其餘函數)就是爲其餘函數添加附加功能
原則:
  • 不能修改被裝飾的函數的源代碼( 再不修改被裝飾函數源代碼的狀況下爲其添加功能)
  • 不能修改被裝飾的函數的調用方式
 
實現裝飾器功能知識儲備:
  • 函數既「變量」 
    • 函數的參數能夠接收變量,也能夠接受函數,這裏引用函數即變量的概念

在python裏面函數就是一個變量,函數名就是一個變量,這個函數名裏面存的是這個函數的內存地址,它把函數體放到內存裏,在調用的時候從函數名裏面的這個內存地址找到函數體而後運行這個函數。前面的博客說函數的時候,說過函數名後面加上小括號就是調用這個函數,若是隻寫這個函數名的話,打印一下就是這個函數的內存地址。python

1 def test():
2     print('Hello Word')
3 
4 
5 print(test)  # 打印函數的內存地址 #<function test at 0x100662e18>
6 test()  # 調用函數
  • 高階函數
    • 把一個函數名當作實參傳給另一個函數
    • 返回值中包含函數名(不修改函數的調用方式) *是否是不理解很懵逼,沒事,一會用到*
    • 當傳遞函數做爲變量時,被稱爲高階函數 
 1 import time
 2 def bar():
 3     time.sleep(3)
 4     print('in the  bar !!')
 5 
 6 def test1(func):
 7     start_tiem = time.time()
 8     func()# run bar()
 9     stop_time = time.time()
10     print('the func run time is %s' %(stop_time-start_tiem))
11 test1(bar)

強行插入知識點: 嵌套函數

 1 def fun():
 2     print('in fun ')
 3     def bar(): #函數的嵌套
 4         print('in the bar ')
 5     bar()
 6 fun()
 7 #--------------------------#
 8 name = "Baylor"
 9 def change_name():
10 
11     name = "Baylor2"
12     def change_name2():
13         name = "Baylor3"
14         print("第3層打", name)
15 
16     change_name2()  # 調用內層函數
17     print("第2層打印", name)
18 
19 change_name()
20 print("最外層打印", name)

嵌套函數中的做用域順序-由內而外的尋找符合要求的參數json

第一個裝飾器

但每一次都須要重新對 test1 進行函數賦值,咱們只須要經過裝飾器的特殊書寫方式,在須要增長新功能的函數上增長「@裝飾器函數名」就能夠完成裝飾器的使用 緩存

 1 def timer(func):
 2     def deco():#函數的嵌套 timer(test1) func = test1
 3         start_time = time.time()
 4         func()  #run test1
 5         stop_time = time.time()
 6         print(' the func time is %s'%(stop_time - start_time))
 7     return deco  #高階函數-
   
   
   
    返回值中包含函數名
 8 
 9 @timer   # test1 = timer(test1)
10 def test1():
11     time.sleep(3)
12     print('in the test1')
13 @timer
14 def test2():
15     time.sleep(2)
16     print('in the test2')
17 
18 #test1 = timer(test1)
19 test1()# --->deco
20 test2()
21 print(timer(test1)) #打印test1 內存地址

因此咱們說:高階函數+嵌套函數 =>裝飾器 ,咱們經過高階函數+函數嵌套實現了以上的裝飾器,他符合不改變調用方式,爲函數增長新功能。數據結構

問題:被裝飾的函數若是有參數呢?app

 1 def w1(func):
 2     def inner(arg):
 3         # 驗證1
 4         # 驗證2
 5         # 驗證3
 6         return func(arg)
 7     return inner
 8 
 9 @w1
10 def f1(arg):
11     print ('f1')
12 
13 
14 def w1(func):
15     def inner(arg1,arg2,arg3):
16         # 驗證1
17         # 驗證2
18         # 驗證3
19         return func(arg1,arg2,arg3)
20     return inner
21 
22 @w1
23 def f1(arg1,arg2,arg3):
24     print ('f1')

問題:能夠裝飾具備處理n個參數的函數的裝飾器?函數

 1 def w1(func):
 2     def inner(*args, **kwargs):
 3         # 驗證1
 4         # 驗證2
 5         # 驗證3
 6         print(*args,**kwargs)
 7 
 8         return func(*args, **kwargs)
 9     return inner
10 
11 @w1
12 def f1(arg1, arg2, arg3):
13     print('f1')
14 
15 f1(1,2,3)

 

若是要裝飾的函數帶有參數時,由於你也不知道到底被裝飾的函數會傳什麼參數,因此可使用可變參數和關鍵字參數來接收全部的參數性能

 1 import time
 2 def timer(func):
 3     def deco(*args,**kwargs):#函數的嵌套 timer(test1) func = test1
 4         start_time = time.time()
 5         func(*args,**kwargs)  #run test1
 6         stop_time = time.time()
 7         print(' the func time is %s'%(stop_time - start_time))
 8     return deco  #高階函數
 9 
10 @timer   # test1 = timer(test1)
11 def test1():
12     time.sleep(3)
13     print('in the test1')
14 @timer  # test2 = timer(test2) = deco test2(name) = deco(name)
15 def test2(name,arg):
16     time.sleep(2)
17     print('in the test2 %s %s',name,arg)
18 
19 test1()# --->deco
20 test2('Baylor',28)

在函數的傳遞參數過程當中,咱們的裝飾器不能知足帶參數的傳遞,以上,實現了當函數有參數傳遞的過程當中也能夠實現裝飾器的新功能

終極函數裝飾-補充知識

 1 import time
 2 user,passwd = 'jinyu','abc123'
 3 def auth(auth_type):
 4     def outer_wrapper(func):
 5         print('auth func:',auth_type)
 6         def wrapper(*args,**kwargs):
 7             print('------',auth_type)
 8             if auth_type =='local':
 9                 username = input('Username:')
10                 password = input('Password')
11                 if user == username and passwd == password:
12 
13                     print('\033[31;1mUser  has password authentication\033[0m')
14                     # res = func(*args,**kwargs)
15                     res = func(*args,**kwargs) #1,2
16                     print('-----afte authentication-----',res)
17                     res.append(3)
18                     print(res)
19                     return res,'方法已運行完畢' # 結果
20                 else:
21                     print('\033[32;1m Invalid username or password\033[0m')
22             elif auth_type =='ldap':
23                 print('哈哈哈哈哈哈!!!ldap')
24         return wrapper
25     return outer_wrapper
26 
27 def index():
28     print('welcome to index page!')
29 @auth(auth_type = 'local')#home  = wraapper
30 def home():
31     print('welcome to home page!')
32     return [1,2]
33 @auth(auth_type = 'ldap')
34 def bbs():
35     print('welcome to bbs page!')
36 
37 index()
38 home()
39 # bbs()

三層函數造成了終結裝飾器,經過裝飾器所傳的參數可以進行裝飾器功能判斷,提供了更好的擴展性 測試

 列表生成式-做用:使代碼更加簡潔

1 a = [i*2 for i in range(10)]   #列表生成式 3行代碼一行搞定
2 print(a)
3 
4 a = []
5 for i in range(10):
6     a.append(i*2)
7 print(a)

生成器

  • 只有在調用時纔會生成相應的數據----生成器:你用到這個裏面的數據的時候它纔會生成,這樣就比較省內存,由於是須要這個值的時候,纔會在內存裏面產生
  • 只記錄當前位置,只有一個__next__()方法 2.7next() 
1 a =(i * 2 for i in range(10)) #若是要打印generator中的元素須要藉助next方法  須要循環才能取出
2 # a =[i * 2 for i in range(10)] # list的元素咱們能夠一個個打印出,
3 print(a) #<generator object <genexpr> at 0x10228f2b0>
4 print(next(a))
5 print(next(a))
6 print(a.__next__())
7 print(a.__next__())

 小練習-斐波那契數列 

 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         print(b)
 5         a, b = b, a + b  # t(b,a+b) a=t[0]-1 b=t[1]-2    a+b等同於 t的下標相加
 6         n = n + 1
 7     return 'done'  # 無用
 8 
 9 
10 fib(10)
11 
12 # 若是把這個改進成 yield 生成器,經過 __next__調用 只須要 把print(b) 改爲 yield b 便可
13 temp = []
14 
15 
16 def fib(max):
17     n, a, b = 0, 0, 1
18     while n < max:
19         yield b
20         a, b = b, a + b  # t(b,a+b) a=t[0]-1 b=t[1]-2    a+b等同於 t的下標相加
21         n = n + 1
22     return 'done'  # 無用
23 
24 x = fib(10)
25 print(x)
26 print(x.__next__())
27 print(x.__next__())
28 print(x.__next__())
29 print(x.__next__()) 

匿名函數 ---也叫 lambda函數

 1 #匿名函數就是不須要顯式的指定函數
 2 
 3 # 這段代碼
 4 def calc(n):
 5     return n * n
 6 
 7 print(calc(10))
 8 
 9 # 換成匿名函數
10 calc = lambda n: n * n
11 print(calc(10))
12 #你也許會說,用上這個東西沒感受有毛方便呀, 。。。。呵呵,若是是這麼用,確實沒毛線改進,不過匿名函數主要是和其它函數搭配使用的呢,以下
13 #x=[1, 5, 7, 4, 8]
14 res = map(lambda x: x * 2, [1, 5, 7, 4, 8]) #map會根據提供的函數對指定序列作映射。
15 for i in res:
16     print(i)
#匿名函數只能處理比較簡單的處理邏輯,只能寫簡單的表達式,不能寫循環 判斷,好比三元運算符

 json 處理

json是一種全部語言中都通用的key-value數據結構的數據類型,很像python中的字典,json處理使用json模塊,json模塊有下面經常使用的方法:ui

 1 import json
 2 
 3 dic = {"name": "niuniu", "age": 18}
 4 print(json.dumps(dic))  # 把字典轉成json串
 5 fj = open('a.json', 'w')
 6 print(json.dump(dic, fj))  # 把字典轉換成的json串寫到一個文件裏面
 7 s_json = '{"name":"niuniu","age":20,"status":true}'
 8 print(json.loads(s_json))  # 把json串轉換成字典
 9 
10 # 先建立b.json文件 運行運行此代碼*{"name": "chenjianguo", "age": 18}*
11 fr = open('b.json', 'r')
12 print(json.load(fr))  # 從文件中讀取json數據,而後轉成字

首先說明基本功能:
  • dumps是將dict轉化成str格式,loads是將str轉化成dict格式。
  • dump和load也是相似的功能,只是與文件操做結合起來了。
  • dump 是把字典轉成son串格式寫   load是把json串轉成字典讀-(json必須雙引號)

  內置函數詳細介紹 https://docs.python.org/3/library/functions.html?highlight=built#ord 

資料補充

生成器並行

 1 import time
 2 def consumer(name):
 3     print('%s 我準備吃包子了'%name)
 4     while True:
 5         baozi = yield #保存當前狀態返回,
 6         print('包子[%s]來了,被[%s]吃了'%(baozi,name))
 7 # c = consumer('houzi')
 8 # c.__next__()
 9 #b = '韭菜餡'
10 #c.send(b) # 調用yield  並傳值
11 #c.__next__()
12 def producer(name):
13     c = consumer('A')  #生成
14     c2 = consumer('B')
15     c.__next__()
16     c2.__next__()
17     print('開始準備作包子了。。。。。。')
18     for i in range(10):
19         time.sleep(1)
20         print('作了2個包子!!!!')
21         c.send(i)
22         c2.send(i)
23 
24 producer('houzi')

 使用類來寫裝飾器-強勢插入新知識

分享一下,須要用類裏面的__call__方法,__call__方法就是能夠把這個實例當成一個函數來調用,若是正常使用類裏面的方法的話,實例方法要先實例化類,而後才能調用,靜態方法、類方法則須要用類名或者實例化以後的對象來調用,而實例化以後的這個對象,是不能被調用的,__call__方法就是把這個實例變成一個能夠調用的對象,也就是說實例化以後,這個對象就能夠和一個普通函數同樣被調用。

1 class Foo:
2     def __call__(self, *args, **kwargs):
3         print('call....')
4     def test(self):#
5         print('test....')
6 if __name__ == '__main__':
7     t = Foo()#實例化類
8     t.test()#正常調用實例方法
9     t()#直接調用實例化以後的對象

理解了上面的以後,就可使用class來寫一個裝飾器了,計算程序的運行時間,固然思想和之前用函數寫裝飾器是同樣的
 1 class Fuck(object):
 2     def __init__(self, func):
 3         self.func = func
 4     def __call__(self, *args, **kwargs):
 5         import time
 6         start_time = time.time()
 7         res = self.func(*args, **kwargs)
 8         end_time = time.time()
 9         print('the function "%s" run time is %s' % (self.func.__name__,
10                                                     (end_time - start_time)))
11         return res
12 @Fuck
13 def run(name):
14     import time
15     time.sleep(1)
16     return 'sb_%s' % name
17 print(run('hyf'))
相關文章
相關標籤/搜索