網站首頁 編程語言 正文
裝飾器
一、介紹
- 器:代表函數的意思。裝飾器本質就是是函數
- 功能:裝飾其他函數,就是為其他函數添加附加功能?
- 被裝飾函數感受不到裝飾器的存在
-
原則:?
不能修改被裝飾的函數的源代碼(比如線上環境)
不能修改被裝飾的函數的調用方式?
-
實現裝飾器知識儲備:?
函數即是“變量”
高階函數
嵌套函數
高階函數+嵌套函數=>裝飾器
二、通過高階函數+嵌套函數==>實現裝飾器
先分析以下兩段代碼能不能運行?
def foo(): print("in the foo") bar() def bar(): print("in the bar") foo()
def foo(): print("in the foo") bar() foo() def bar(): print("in the bar")
第二段代碼報錯:
NameError: name 'bar' is not defined
1、變量知識回顧
定義變量:?
如:定義變量:x=1,會在內存中找塊內存空間把“1”存進去,把“1”的內存地址給x??
前面提到:函數即變量
# 定義函數 def test(): pass # 就相當于把函數體賦值給test變量 test = '函數體' # 函數體就是一堆字符串而已 # 只不過函數調用要加上小括號調用 test()
python內存回收機制,是解釋器做的。解釋器到底怎么去回收這個變量??
python解釋器當中有種概念叫做引用計數。什么叫引用計數呢??
比如:定義x=1,之后又定義了y=1或y=x,實際上又把內存空間“1”的內存地址賦值給y?
這里x代表一次引用,y代表一次引用。加起來兩次引用。?
python什么時候會把“1”這個內存空間清空呢?會回收內存呢??
當x這個變量沒有了,y這個變量也沒有了,便會把“1”這個內存空間清掉
del x # 刪的只是變量名,內存中的值是解釋器回收
匿名函數
lambda x:x*x
匿名函數沒有函數名,沒有引用,所以會被垃圾回收機制立馬回收掉。?
所以匿名函數要賦值給變量,把函數體賦值給變量名
calc = lambda x:x*x print(calc(4))
現在可以再理解下最開始兩段代碼能不能運行的原因。?
2、高階函數(裝飾器前奏)
什么叫高階函數呢:
- 把一個函數名當做形實傳給另外一個函數
- 返回值中包含函數名
def f1(): print("in the func1") def test1(func): print(func) test1(f1)
運行結果(打印內存地址)
<function func1 at 0x000002805DE12378>
如下代碼,能不能運行:
def f1(): print("in the func1") def test1(func): print(func) func() test1(f1)
函數即變量,像“x=1,y=x”,同樣f是一個是一個函數,可不可以像一個變量一樣來回賦值呢?
import time def func1(): print("in the func1") time.sleep(1) def test1(func): start_time = time.time() func() stop_time = time.time() print("the func run time is %s" %(stop_time-start_time)) test1(func1)
到這里,貌似實現了裝飾函數的功能。?
看上面裝飾器的原則:?
這里:沒有修改func1的源代碼,但是調用方式改變了。現在是test1(func1),之前是func1()?
現在能做到哪一點呢??
把一個函數名當做實參傳給另外一個函數(不修改被裝飾的函數源代碼的情況下為其添加功能)
2) 下面用第二個條件(返回值中包含函數名),做另外一個高階函數
import time def func2(): time.sleep(1) print("in the func2") def test2(func): print(func) return(func) print(test2(func2))
運行結果:
<function func2 at 0x00000162F3672378>
<function func2 at 0x00000162F3672378>
把函數內存地址都打印出來了,看到這么多內存地址,有什么想法??
加上小括號就能運行。?
上面代碼“test2(func2())”和“test2(func2)”有什么區別?加上小括號是函數返回結果,不加是函數內存地址。所以加上小括號就不符合高階函數定義了。?
既然以后有了函數的內存地址,是不是可以賦值給其他變量?下面
import time def func2(): print("in the func2") time.sleep(1) def test2(func): print(func) return(func) t = test2(func2) print(t) t()
好像還沒什么用,怎么讓他有用呢??
把test2(func2)賦值給func2
import time def func2(): print("in the func2") time.sleep(1) def test2(func): print(func) return(func) func2 = (test2(func2)) func2()
這就是高階函數的第二個好處:返回值中包含函數名(不修改函數的調用方式)
3、嵌套函數(裝飾器前戲)
嵌套函數:在一個函數體內,用def去聲明一個函數
def foo(): print("in the foo") def bar(): print("in the bar") bar() foo()
看一下下面的代碼是不是嵌套:
def foo(): print("in the foo") def bar(): foo() bar()
注意函數嵌套和函數調用區別??
局部作用域和全局作用域的訪問順序:
x = 0 def grandpa(): # x = 1 def dad(): x = 2 def son(): x = 3 print(x) son() dad() grandpa()
三、裝飾器
1、裝飾器
前面鋪墊了那么多,現在開講正題:裝飾器?
先用高階函數實現給函數不修改源代碼的情況下添加功能
import time def deco(func): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) def test1(): time.sleep(1) print("in the test1") def test2(): time.sleep(1) print("in the test2") deco(test1) deco(test2)
按照上面說的,如何實現不改變調用方式?直接“test1 = deco(test1)”和“test2 = deco(test2)”嗎??
別忘記了,第二種方式,高階函數要加上return,如下
import time def deco(func): start_time = time.time() return func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) def test1(): time.sleep(1) print("in the test1") def test2(): time.sleep(1) print("in the test2") test1 = deco(test1) test2 = deco(test2) deco(test1) deco(test2)
雖然沒有修改源代碼和調用方式,但是函數加上return,函數就結束了,然并卵。怎么實現呢??
前面一直在用高階函數,還沒有用嵌套函數,加上嵌套函數能不能實現呢?看一下
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的內存地址 def test1(): time.sleep(1) print("in the test1") def test2(): time.sleep(1) print("in the test2") print(timer(test1)) # 可見:返回deco的內存地址 test1 = timer(test1) test1() timer(test2)()
到此,完成實現了裝飾器的功能。但是還是有點麻煩,如何能不要“test1 = timer(test1)”,?
python解釋器提供了語法糖“@”符合,給哪個函數新增功能,就加在哪個函數頭部
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的內存地址 @timer def test1(): time.sleep(1) print("in the test1") @timer def test2(): time.sleep(1) print("in the test2") test1() test2()
2、有參裝飾器
前面實現了裝飾器的功能,但是如果函數有參數,能不能也能運行呢
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的內存地址 @timer def test1(): time.sleep(1) print("in the test1") @timer def test2(name): time.sleep(1) print("in the test2",name) test1() test2()
報錯:丟失參數
TypeError: test2() missing 1 required positional argument: 'name'
@timer 相當于 test2=timer(test2) =deco?
test2() 相當于運行deco(),所以沒指定參數,報錯。?
如何傳參數呢?為了適應各種不同參數的函數
import time def timer(func): # timer(test1) func=test1 def deco(*args,**kwargs): start_time = time.time() func(*args,**kwargs) stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的內存地址 @timer def test1(): time.sleep(1) print("in the test1") @timer def test2(name): time.sleep(1) print("in the test2",name) test1() test2("fgf")
3、終極裝飾器
注意,上面的例子中還沒有涉及返回值,看下面的例子可以體會一下?
假設:公司網站需要驗證登錄,有不同的驗證方式:本地認證、LDAP認證等
#/usr/bin/env python # -*- coding: UTF-8 -*- import time user,passwd = 'fgf','abc123' def auth(auth_type): print("auth func:",auth_type) def outer_wrapper(func): def wrapper(*args, **kwargs): print("wrapper func args:", *args, **kwargs) if auth_type == "local": username = input("Username:").strip() password = input("Password:").strip() if user == username and passwd == password: print("\033[32;1mUser has passed authentication\033[0m") res = func(*args, **kwargs) # from home print("---after authenticaion ") return res else: exit("\033[31;1mInvalid username or password\033[0m") elif auth_type == "ldap": print("搞毛線ldap,不會。。。。") return wrapper return outer_wrapper def index(): print("welcome to index page") @auth(auth_type="local") # home = wrapper() def home(): print("welcome to home page") return "from home" @auth(auth_type="ldap") def bbs(): print("welcome to bbs page") index() print(home()) #wrapper() bbs()
原文鏈接:https://www.cnblogs.com/leixiaobai/articles/8031391.html
相關推薦
- 2022-12-11 Dart多個future隊列完成加入順序關系及原子性論證_Dart
- 2022-01-19 標準時間格式轉換(正則寫法)
- 2022-03-09 C語言直接插入排序算法介紹_C 語言
- 2022-08-22 Pycharm報錯Non-zero?exit?code?(2)的完美解決方案_python
- 2022-09-30 關于react中useCallback的用法_React
- 2022-09-13 C#中的modbus?Tcp協議的數據抓取和使用解析_C#教程
- 2021-11-02 Linux環境下生成openssl證書注意細節介紹_Linux
- 2024-03-01 【Typescript】tsconfig 常用配置項(常用or總結)
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支