装饰器,生成器,迭代器
文章目錄
- 裝飾器
- 什么是裝飾器
- 為什么使用裝飾器
- 裝飾器的使用場景
- 用戶認(rèn)證,判斷用戶是否登錄
- 計(jì)算函數(shù)運(yùn)行時(shí)間(算是一個(gè)功能,在項(xiàng)目中用的不多)
- 記錄日志
- redis緩存
- 裝飾器1----能夠適應(yīng)90%的業(yè)務(wù)需求
- 裝飾器2----對(duì)特定網(wǎng)頁進(jìn)行身份驗(yàn)證
- 裝飾器3----終極篇:實(shí)現(xiàn)對(duì)不同網(wǎng)頁不同方式的身份認(rèn)證
- 三級(jí)裝飾器
- 常用的內(nèi)置裝飾器
- 使用閉包實(shí)現(xiàn)裝飾器功能
- python裝飾器補(bǔ)充之functools包中的wraps
- 生成器
- 生成器的定義
- 為什么使用生成器
- 生成器的工作原理
- yield生成器運(yùn)行機(jī)制
- yield實(shí)現(xiàn)單線程下的并發(fā)效果
- 迭代器
- 迭代器的定義
- 迭代器的兩個(gè)基本方法:
- 迭代器和生成器的區(qū)別
裝飾器
什么是裝飾器
- 裝飾器本身是函數(shù),用來給其他函數(shù)添加新的功能
- 特點(diǎn):不修改調(diào)用方式,不修改源代碼
為什么使用裝飾器
- 使用方便
- 節(jié)省開發(fā)時(shí)間
裝飾器的使用場景
用戶認(rèn)證,判斷用戶是否登錄
user,passwd = 'aaa','123' def auth(func):def wrapper(username,password,*args,**kwargs):if user == username and password == passwd:print("User has passed authentication")res = func(username,password,*args,**kwargs) #這里執(zhí)行func()相當(dāng)于執(zhí)行調(diào)用的函數(shù)如home()return res #為了獲得home()函數(shù)返回值,可以將執(zhí)行結(jié)果賦值給res然后返回print(home())結(jié)果是"from home"而不是"None"了else:raise ValueError("非合法用戶")return wrapper@auth def home(username,password):print("welcome to home page")return "from home"home('aaa','123')計(jì)算函數(shù)運(yùn)行時(shí)間(算是一個(gè)功能,在項(xiàng)目中用的不多)
記錄日志
#! /usr/bin/env python # -*- coding: utf-8 -*- from functools import wraps import traceback def decoratore(func):@wraps(func)def log(*args,**kwargs):try:print("當(dāng)前運(yùn)行方法",func.__name__)return func(*args,**kwargs)except Exception as e:print(traceback.format_exc()) # 這里應(yīng)該調(diào)用log模塊來記錄到日志里return log@decoratore def test():int('a')passif __name__ == '__main__':test()''' 上面運(yùn)行結(jié)果當(dāng)前運(yùn)行方法 testTraceback (most recent call last):File "C:/Users/tom/Desktop/alipay_demo/aaa/t2.py", line 11, in logreturn func(*args,**kwargs)File "C:/Users/tom/Desktop/alipay_demo/aaa/t2.py", line 18, in testint('a')ValueError: invalid literal for int() with base 10: 'a'22222'''redis緩存
第一步:查詢r(jià)edis緩存是否存在這個(gè)key
第二步:如果存在這個(gè)key,不用去mysql中查詢,直接從redis中取出數(shù)據(jù)即可(減輕了mysql壓力)
第三步:如果查詢的key不存在,然后到mysql中查詢數(shù)據(jù),讓后設(shè)置到redis中,下次查詢就有了
裝飾器1----能夠適應(yīng)90%的業(yè)務(wù)需求
在裝飾器中 @timer等價(jià)于 test1=timer(test1)
1. 在timer()函數(shù)中返回值是return deco
2. 所以timer(test1)作用是將函數(shù)test1內(nèi)存地址當(dāng)做參數(shù)傳遞給timer()
3. timer() 函數(shù)最后將運(yùn)行后的函數(shù)deco內(nèi)存地址作為返回值返回
4. test1=timer(test1)作用就是將將deco函數(shù)內(nèi)存地址賦值給test1,所以最后運(yùn)行test1()就相當(dāng)于運(yùn)行deco()
5. 所以最后調(diào)用時(shí)給test2()傳入?yún)?shù)就相當(dāng)于給deco傳入?yún)?shù)
import time def timer(func): #timer(test1) func=test1def deco(*args,**kwargs):start_time = time.time()func(*args,**kwargs) #run test1stop_time = time.time()print("running time is %s"%(stop_time-start_time))return deco @timer # test1=timer(test1) def test1():time.sleep(3)print("in the test1") @timer def test2(name):print("in the test2",name) test1() test2("tom")裝飾器2----對(duì)特定網(wǎng)頁進(jìn)行身份驗(yàn)證
import time user,passwd = 'aaa','123' def auth(func):def wrapper(*args,**kwargs):username = input("Username:").strip()password = input("Password:").strip()if user == username and password == passwd:print("User has passed authentication")res = func(*args,**kwargs) #這里執(zhí)行func()相當(dāng)于執(zhí)行調(diào)用的函數(shù)如home()return res #為了獲得home()函數(shù)返回值,可以將執(zhí)行結(jié)果賦值給res然后返回print(home())結(jié)果是"from home"而不是"None"了else:exit("Invalid username or password")return wrapper def index():print("welcome to index page") @auth def home():print("welcome to home page")return "from home" @auth def bbs():print("welcome to bbs page") index() print(home()) #在這里調(diào)用home()相當(dāng)于調(diào)用wrapper() bbs()裝飾器3----終極篇:實(shí)現(xiàn)對(duì)不同網(wǎng)頁不同方式的身份認(rèn)證
@auth(auth_type=“l(fā)ocal”)代碼作用
- 在上面的代碼中使用@auth相當(dāng)于先將home函數(shù)的內(nèi)存地址當(dāng)做變量傳入auth()函數(shù),執(zhí)行結(jié)果后home()相當(dāng)于wrapper()
- 而在這里驗(yàn)證的時(shí)候猶豫@auth(auth_type=“l(fā)ocal”)中有()括號(hào),那么就相當(dāng)于將執(zhí)行auth()函數(shù)而且是將auth_type="local當(dāng)做參數(shù)傳入到auth()函數(shù)執(zhí)行
- 所以outer_wrapper函數(shù)也會(huì)執(zhí)行,outer_wrapper函數(shù)的執(zhí)行結(jié)果返回的就是wrapper()函數(shù)的內(nèi)存地址
- 所以最終結(jié)果同樣是執(zhí)行home()函數(shù)就相當(dāng)于執(zhí)行wrapper函數(shù)
- 但是有所不同的是著這個(gè)版本中我們可以在外層的auth函數(shù)中傳入新的參數(shù)幫組我們根據(jù)需求判斷
三級(jí)裝飾器
#! /usr/bin/env python # -*- coding: utf-8 -*- import time def auth(auth_type):print("auth func:",auth_type)def outer_wrapper(func):def wrapper(*args, **kwargs):print("wrapper func args:", *args, **kwargs)print('運(yùn)行前')func(*args, **kwargs)print('運(yùn)行后')return wrapperreturn outer_wrapper@auth(auth_type="local") # home = wrapper() def home():print("welcome to home page")return "from home" home()常用的內(nèi)置裝飾器
- staticmethod
類似實(shí)現(xiàn)了靜態(tài)方法 注入以后,可以直接 : 類名.方法 - property
經(jīng)過property裝飾過的函數(shù) 不再是一個(gè)函數(shù),而是一個(gè)property,類似實(shí)現(xiàn)get,set方法
使用閉包實(shí)現(xiàn)裝飾器功能
閉包概念
- 在一個(gè)外函數(shù)中定義了一個(gè)內(nèi)函數(shù),內(nèi)函數(shù)里運(yùn)用了外函數(shù)的臨時(shí)變量,并且外函數(shù)的返回值是內(nèi)函數(shù)的引用,這樣就構(gòu)成了一個(gè)閉包
- 一般情況下,在我們認(rèn)知當(dāng)中,如果一個(gè)函數(shù)結(jié)束,函數(shù)的內(nèi)部所有東西都會(huì)釋放掉,還給內(nèi)存,局部變量都會(huì)消失
- 但是閉包是一種特殊情況,如果外函數(shù)在結(jié)束的時(shí)候發(fā)現(xiàn)有自己的臨時(shí)變量將來會(huì)在內(nèi)部函數(shù)中用到,就把這個(gè)臨時(shí)變量綁定給了內(nèi)部函數(shù),然后自己再結(jié)束
python裝飾器補(bǔ)充之functools包中的wraps
Python被裝飾函數(shù)其實(shí)已經(jīng)是另外一個(gè)函數(shù)
#! /usr/bin/env python # -*- coding: utf-8 -*- def login_required(view_func):def wrapper(*args,**kwargs):passreturn wrapper@login_required def test1():'''test1...'''print('test1')print (test1.__name__) # 結(jié)果:wrapper (標(biāo)識(shí)test1函數(shù)已經(jīng)變成wrapper函數(shù)了)使用functools的wrap,它能保留原有函數(shù)的名稱
#! /usr/bin/env python # -*- coding: utf-8 -*- from functools import wrapsdef login_required(view_func):@wraps(view_func)def wrapper(*args,**kwargs):passreturn wrapper@login_required def test1():'''test1...'''print('test1')print (test1.__name__) # 結(jié)果:test1 (解決了改變函數(shù)的問題,能保留原有函數(shù)的名稱)生成器
生成器的定義
- 生成器,即生成一個(gè)容器
- 生成器就是一個(gè)特殊的迭代器
- 在Python中,一邊循環(huán),一邊計(jì)算的機(jī)制,稱為生成器。
- 生成器可以理解為一種數(shù)據(jù)類型,這種數(shù)據(jù)類型自動(dòng)實(shí)現(xiàn)了迭代器協(xié)議(其他數(shù)據(jù)類型需要調(diào)用自己的內(nèi)置iter() 方法或__iter__()的內(nèi)置函數(shù))所以,生成器就是一個(gè)可迭代對(duì)象。
為什么使用生成器
- 節(jié)省空間
- 高效
生成器的工作原理
- 生成器是這樣一個(gè)函數(shù),它記住上一次返回時(shí)在函數(shù)體中的位置
- 對(duì)生成器函數(shù)的第二次(或第 n 次)調(diào)用跳轉(zhuǎn)至該函數(shù)中間,而上次調(diào)用的所有局部變量都保持不變
- 生成器不僅“記住”了它數(shù)據(jù)狀態(tài);生成器還“記住”了它在流控制構(gòu)造(在命令式編程中,這種構(gòu)造不只是數(shù)據(jù)值)中的位置
- 生成器是一個(gè)函數(shù),而且函數(shù)的參數(shù)都會(huì)保留
- 迭代到下一次的調(diào)用時(shí),所使用的參數(shù)都是第一次所保留下的,即是說,在整個(gè)所有函數(shù)調(diào)用的參數(shù)都是第一次所調(diào)用時(shí)保留的,而不是新創(chuàng)建的
生成器的場景應(yīng)用?
比如我們要生成一百萬個(gè)數(shù)據(jù),如果用生成器非常節(jié)省空間,用列表浪費(fèi)大量空間
yield生成器運(yùn)行機(jī)制
使用yield函數(shù)實(shí)現(xiàn)斐波那契數(shù)列
def fib(max_num):a,b = 1,1while a < max_num:yield ba,b=b,a+bg = fib(10) #生成一個(gè)生成器:[2, 3, 5, 8, 13] print(g.__next__()) #第一次調(diào)用返回:1 print(list(g))生成器讀寫文件:
#!/usr/bin/python # -*- coding: utf-8 -*- def read_big_file_v(fname):block_size = 1024 * 8with open(fname,encoding="utf8") as fp:while True:chunk = fp.read(block_size)# 當(dāng)文件沒有更多內(nèi)容時(shí),read 調(diào)用將會(huì)返回空字符串 ''if not chunk:breakprint(chunk) path = r'C:\aaa\luting\edc-backend\tttt.py' read_big_file_v(path)yield實(shí)現(xiàn)單線程下的并發(fā)效果
- yield相當(dāng)于 return 返回一個(gè)值,并且記住這個(gè)返回的位置,下次迭代時(shí),代碼從yield的下一條語句開始執(zhí)行。
- send() 和next()一樣,都能讓生成器繼續(xù)往下走一步(下次遇到y(tǒng)ield停),但send()能傳一個(gè)值,這個(gè)值作為yield表達(dá)式整體的結(jié)果
迭代器
迭代器的定義
- 迭代器是訪問集合元素的一種方式
- 所謂的迭代器就是具有next方法的對(duì)象。
- 迭代器對(duì)象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束。迭代器只能往前不會(huì)后退。使用inter()函數(shù)創(chuàng)建迭代器
- 迭代器僅是一容器對(duì)象,它實(shí)現(xiàn)了迭代器協(xié)議
迭代器的兩個(gè)基本方法:
① next方法
返回容器的下一個(gè)元素
② __iter__方法
返回迭代器自身
- 在調(diào)用next方法時(shí),迭代器會(huì)返回它的下一個(gè)值,如果next方法被調(diào)用,但迭代器中沒有值可以返就會(huì)引發(fā)一個(gè)StopIteration異常
迭代器和可迭代對(duì)象
迭代器和生成器的區(qū)別
- 在使用生成器時(shí),創(chuàng)建一個(gè)函數(shù),在使用迭代器時(shí),使用內(nèi)置函數(shù)iter()和next(),在生成器中,使用關(guān)鍵字‘yield’來每次生成/返回一個(gè)對(duì)象
- 每次’yield’暫停循環(huán)時(shí),生成器會(huì)保存本地變量的狀態(tài),而迭代器并不會(huì)使用局部變量,只需要一個(gè)可迭代對(duì)象進(jìn)行迭代
- 使用類可以實(shí)現(xiàn)迭代器,但無法實(shí)現(xiàn)生成器,生成器運(yùn)行速度快,語法簡單,迭代器更能節(jié)約內(nèi)存
判斷是迭代器和可迭代對(duì)象
注:列表,元組,字典是可迭代的但不是迭代器
from collections import Iterable print(isinstance([],Iterable)) #True print(isinstance({},Iterable)) #True print(isinstance((),Iterable)) #True print(isinstance("aaa",Iterable)) #True print(isinstance((x for x inrange(10)),Iterable)) #True列表不是迭代器,只有生成器是迭代器
from collections import Iterator t = [1,2,3,4] print(isinstance(t,Iterator)) #False t1 = iter(t) print(isinstance(t1,Iterator)) #True自定義迭代器
class MyRange(object):def __init__(self, n):self.idx = 0self.n = ndef __iter__(self):return selfdef next(self):if self.idx < self.n:val = self.idxself.idx += 1return self.n[val]else:raise StopIteration()l = [4,5,6,7,8] obj = MyRange(l) print obj.next() # 4 print obj.next() # 5 print obj.next() # 6總結(jié)
以上是生活随笔為你收集整理的装饰器,生成器,迭代器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程,线程,协程
- 下一篇: 面向对象封装继承多态五大基本原则魔法方法