装饰器,生成器,迭代器
文章目錄
- 裝飾器
- 什么是裝飾器
- 為什么使用裝飾器
- 裝飾器的使用場景
- 用戶認證,判斷用戶是否登錄
- 計算函數運行時間(算是一個功能,在項目中用的不多)
- 記錄日志
- redis緩存
- 裝飾器1----能夠適應90%的業務需求
- 裝飾器2----對特定網頁進行身份驗證
- 裝飾器3----終極篇:實現對不同網頁不同方式的身份認證
- 三級裝飾器
- 常用的內置裝飾器
- 使用閉包實現裝飾器功能
- python裝飾器補充之functools包中的wraps
- 生成器
- 生成器的定義
- 為什么使用生成器
- 生成器的工作原理
- yield生成器運行機制
- yield實現單線程下的并發效果
- 迭代器
- 迭代器的定義
- 迭代器的兩個基本方法:
- 迭代器和生成器的區別
裝飾器
什么是裝飾器
- 裝飾器本身是函數,用來給其他函數添加新的功能
- 特點:不修改調用方式,不修改源代碼
為什么使用裝飾器
- 使用方便
- 節省開發時間
裝飾器的使用場景
用戶認證,判斷用戶是否登錄
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) #這里執行func()相當于執行調用的函數如home()return res #為了獲得home()函數返回值,可以將執行結果賦值給res然后返回print(home())結果是"from home"而不是"None"了else:raise ValueError("非合法用戶")return wrapper@auth def home(username,password):print("welcome to home page")return "from home"home('aaa','123')計算函數運行時間(算是一個功能,在項目中用的不多)
記錄日志
#! /usr/bin/env python # -*- coding: utf-8 -*- from functools import wraps import traceback def decoratore(func):@wraps(func)def log(*args,**kwargs):try:print("當前運行方法",func.__name__)return func(*args,**kwargs)except Exception as e:print(traceback.format_exc()) # 這里應該調用log模塊來記錄到日志里return log@decoratore def test():int('a')passif __name__ == '__main__':test()''' 上面運行結果當前運行方法 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緩存
第一步:查詢redis緩存是否存在這個key
第二步:如果存在這個key,不用去mysql中查詢,直接從redis中取出數據即可(減輕了mysql壓力)
第三步:如果查詢的key不存在,然后到mysql中查詢數據,讓后設置到redis中,下次查詢就有了
裝飾器1----能夠適應90%的業務需求
在裝飾器中 @timer等價于 test1=timer(test1)
1. 在timer()函數中返回值是return deco
2. 所以timer(test1)作用是將函數test1內存地址當做參數傳遞給timer()
3. timer() 函數最后將運行后的函數deco內存地址作為返回值返回
4. test1=timer(test1)作用就是將將deco函數內存地址賦值給test1,所以最后運行test1()就相當于運行deco()
5. 所以最后調用時給test2()傳入參數就相當于給deco傳入參數
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----對特定網頁進行身份驗證
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) #這里執行func()相當于執行調用的函數如home()return res #為了獲得home()函數返回值,可以將執行結果賦值給res然后返回print(home())結果是"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()) #在這里調用home()相當于調用wrapper() bbs()裝飾器3----終極篇:實現對不同網頁不同方式的身份認證
@auth(auth_type=“local”)代碼作用
- 在上面的代碼中使用@auth相當于先將home函數的內存地址當做變量傳入auth()函數,執行結果后home()相當于wrapper()
- 而在這里驗證的時候猶豫@auth(auth_type=“local”)中有()括號,那么就相當于將執行auth()函數而且是將auth_type="local當做參數傳入到auth()函數執行
- 所以outer_wrapper函數也會執行,outer_wrapper函數的執行結果返回的就是wrapper()函數的內存地址
- 所以最終結果同樣是執行home()函數就相當于執行wrapper函數
- 但是有所不同的是著這個版本中我們可以在外層的auth函數中傳入新的參數幫組我們根據需求判斷
三級裝飾器
#! /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('運行前')func(*args, **kwargs)print('運行后')return wrapperreturn outer_wrapper@auth(auth_type="local") # home = wrapper() def home():print("welcome to home page")return "from home" home()常用的內置裝飾器
- staticmethod
類似實現了靜態方法 注入以后,可以直接 : 類名.方法 - property
經過property裝飾過的函數 不再是一個函數,而是一個property,類似實現get,set方法
使用閉包實現裝飾器功能
閉包概念
- 在一個外函數中定義了一個內函數,內函數里運用了外函數的臨時變量,并且外函數的返回值是內函數的引用,這樣就構成了一個閉包
- 一般情況下,在我們認知當中,如果一個函數結束,函數的內部所有東西都會釋放掉,還給內存,局部變量都會消失
- 但是閉包是一種特殊情況,如果外函數在結束的時候發現有自己的臨時變量將來會在內部函數中用到,就把這個臨時變量綁定給了內部函數,然后自己再結束
python裝飾器補充之functools包中的wraps
Python被裝飾函數其實已經是另外一個函數
#! /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__) # 結果:wrapper (標識test1函數已經變成wrapper函數了)使用functools的wrap,它能保留原有函數的名稱
#! /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__) # 結果:test1 (解決了改變函數的問題,能保留原有函數的名稱)生成器
生成器的定義
- 生成器,即生成一個容器
- 生成器就是一個特殊的迭代器
- 在Python中,一邊循環,一邊計算的機制,稱為生成器。
- 生成器可以理解為一種數據類型,這種數據類型自動實現了迭代器協議(其他數據類型需要調用自己的內置iter() 方法或__iter__()的內置函數)所以,生成器就是一個可迭代對象。
為什么使用生成器
- 節省空間
- 高效
生成器的工作原理
- 生成器是這樣一個函數,它記住上一次返回時在函數體中的位置
- 對生成器函數的第二次(或第 n 次)調用跳轉至該函數中間,而上次調用的所有局部變量都保持不變
- 生成器不僅“記住”了它數據狀態;生成器還“記住”了它在流控制構造(在命令式編程中,這種構造不只是數據值)中的位置
- 生成器是一個函數,而且函數的參數都會保留
- 迭代到下一次的調用時,所使用的參數都是第一次所保留下的,即是說,在整個所有函數調用的參數都是第一次所調用時保留的,而不是新創建的
生成器的場景應用?
比如我們要生成一百萬個數據,如果用生成器非常節省空間,用列表浪費大量空間
yield生成器運行機制
使用yield函數實現斐波那契數列
def fib(max_num):a,b = 1,1while a < max_num:yield ba,b=b,a+bg = fib(10) #生成一個生成器:[2, 3, 5, 8, 13] print(g.__next__()) #第一次調用返回: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)# 當文件沒有更多內容時,read 調用將會返回空字符串 ''if not chunk:breakprint(chunk) path = r'C:\aaa\luting\edc-backend\tttt.py' read_big_file_v(path)yield實現單線程下的并發效果
- yield相當于 return 返回一個值,并且記住這個返回的位置,下次迭代時,代碼從yield的下一條語句開始執行。
- send() 和next()一樣,都能讓生成器繼續往下走一步(下次遇到yield停),但send()能傳一個值,這個值作為yield表達式整體的結果
迭代器
迭代器的定義
- 迭代器是訪問集合元素的一種方式
- 所謂的迭代器就是具有next方法的對象。
- 迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會后退。使用inter()函數創建迭代器
- 迭代器僅是一容器對象,它實現了迭代器協議
迭代器的兩個基本方法:
① next方法
返回容器的下一個元素
② __iter__方法
返回迭代器自身
- 在調用next方法時,迭代器會返回它的下一個值,如果next方法被調用,但迭代器中沒有值可以返就會引發一個StopIteration異常
迭代器和可迭代對象
迭代器和生成器的區別
- 在使用生成器時,創建一個函數,在使用迭代器時,使用內置函數iter()和next(),在生成器中,使用關鍵字‘yield’來每次生成/返回一個對象
- 每次’yield’暫停循環時,生成器會保存本地變量的狀態,而迭代器并不會使用局部變量,只需要一個可迭代對象進行迭代
- 使用類可以實現迭代器,但無法實現生成器,生成器運行速度快,語法簡單,迭代器更能節約內存
判斷是迭代器和可迭代對象
注:列表,元組,字典是可迭代的但不是迭代器
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總結
以上是生活随笔為你收集整理的装饰器,生成器,迭代器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程,线程,协程
- 下一篇: 面向对象封装继承多态五大基本原则魔法方法