tft每日頭條

 > 生活

 > python裝飾器簡明教程

python裝飾器簡明教程

生活 更新时间:2024-06-23 11:03:24

python裝飾器簡明教程?目錄一、裝飾器概述,接下來我們就來聊聊關于python裝飾器簡明教程?以下内容大家不妨參考一二希望能幫到您!

python裝飾器簡明教程(終于搞明白了Python裝飾器)1

python裝飾器簡明教程

目錄

一、裝飾器概述

二、裝飾器的入門

1.什麼是閉包

2.什麼是裝飾器

3.閉包與裝飾器的區别

4.為何要用裝飾器

5.适用場景

三、如何使用裝飾器

1.通過案例了解裝飾器的用法

2.Python裝飾器執行順序詳解

四、裝飾器傳參

五、裝飾器返回值

六、通用裝飾器

七、裝飾器帶參數


一、裝飾器概述

裝飾器是一個函數,該函數是用來為其他函數添加額外的功能,就是拓展原來函數功能的一種函數。


二、裝飾器的入門1.什麼是閉包

要掌握裝飾器先得理解閉包,掌握了閉包以後再學裝飾器就很容易了。

閉包就是外部函數中定義一個内部函數,内部函數引用外部函數中的變量,外部函數的返回值是内部函數。閉包是将函數内部和函數外部連接起來的橋梁。

舉例:比如我們調用一個帶有返回值的函數x,此時函數x為我們返回一個函數y,這個函數y,就被稱為閉包。

# 閉包 def out(i): # 一個外層函數 def inner(j): # 内層函數 return i*j return inner

閉包定義條件:

  • 必須有一個内嵌函數
  • 内嵌函數必須引用外部函數中的變量
  • 外部函數的返回值必須是内嵌函數

案例:函數test_in就是閉包

def test(number): print("--1--") def test_in(number2): print("--2--") print(number number2) print("--3--") return test_in ret = test(100) #先執行第3行,輸出"--1--";再執行第5行,函數名(test_in),不執行函數内部;然後執行第10行,輸出"--3--";然後返回(test_in)函數給(test)函數,賦值給ret對象。 print("-"*30) #輸出”------------------------------“ ret(1) #執行(test_in)函數,執行第6行,輸出"--2--";執行第7行,輸出"101"(number=100,number2=1)。 ret(100) #執行(test_in)函數,執行第6行,輸出"--2--";執行第7行,輸出"200"(number=100,number2=100)。 ret(200) #執行(test_in)函數,執行第6行,輸出"--2--";執行第7行,輸出"300"(number=100,number2=200)。

輸出結果:

--1-- --3-- ------------------------------ --2-- 101 --2-- 200 --2-- 300

運行分析:先調用外部函數test傳入默認值number,用ret去指向返回函數内部的引用。後面在調用ret的時候,就會在調用外面函數的基礎上進行計算。

2.什麼是裝飾器

裝飾器就是裝飾、裝修的意思,不改變原有程序的功能。比如,我家有一個房子,如果不隔音,我在牆上加一層隔音闆,而不是重新再建一座房子。這樣一來,房子還是原來的,隻是增加了一個隔音闆(裝飾器),實現了房子隔音的功能。

在程序中也是一樣,不會對原來的函數造成變化,還要添加新的功能,調用函數時的接口沒有變化。

裝飾器可以基于函數實現,也可以基于類實現,其使用方式基本是固定的。它的基本步驟為:

  1. 定義裝飾函數(類)
  2. 定義業務函數
  3. 在業務函數上一行添加@裝飾函數名/類名

案例:(需求:在不動原函數的基礎上增加新的功能)

def w1(func): """裝飾器函數""" def inner(): func() print("這是添加的新功能") return inner @w1 # 等同于調用函數的時候上方加上:f1 = w1(f1) def f1(): """業務函數""" print("---f1---") @w1 # 等同于調用函數的時候上方加上:f2 = w1(f2) def f2(): """業務器函數""" print("---f2---") f1() f2()

輸出結果:

---f1--- 這是添加的新功能 ---f2--- 這是添加的新功能

3.閉包與裝飾器的區别

閉包傳遞的是 變量,而裝飾器傳遞的是 函數,除此之外沒有任何區别,或者說裝飾器是閉包的一種,它隻是 傳遞函數的閉包。

4.為何要用裝飾器

開放封閉原則開放:指的是對拓展功能是開放的封閉:指的是對修改源代碼是封閉的

裝飾器就是在不修改被裝飾器對象源代碼以及調用方式的前提下,為被裝飾對象添加新功能。

5.适用場景

函數在執行前後定義一些功能。


三、如何使用裝飾器1.通過案例了解裝飾器的用法

案例:

def w1(fn): """裝飾器函數""" print("---正在裝飾---") def inner(): print("---正在驗證---") fn() return inner @w1 # 隻要Python解釋器執行到這個代碼,就開始自動進行裝飾,而不是等到調用的時候才裝飾 def f1(): """業務函數""" print("---2---") return "hello python"

輸出結果:

---正在裝飾---

運行分析:

代碼執行到@w1就開始裝飾了,我們并沒有調用函數f1都輸出了“---正在裝飾---”,我們調用的是裝飾後的結果。


案例:

def w1(fn): """裝飾器函數""" print("---正在裝飾1---") def inner(): print("---正在驗證---") fn() return inner def w2(fn): """裝飾器函數""" print("---正在裝飾2---") def inner(): print("---正在驗證---") fn() return inner @w1 @w2 def f1(): """業務函數""" print("---3---")

輸出結果:

---正在裝飾2--- ---正在裝飾1---

運行分析:

@w1在最上面,下面需要是一個函數,可下面是@w2,必須等@w2裝飾完再裝飾@w1,所以先輸出 ---正在裝飾2---,再輸出 ---正在裝飾1---。

2.Python裝飾器執行順序詳解

2.1案例執行

案例:

def decorator_a(func): print ('Get in decorator_a') def inner_a(*args, **kwargs): print ('Get in inner_a') return func(*args, **kwargs) return inner_a def decorator_b(func): print ('Get in decorator_b') def inner_b(*args, **kwargs): print ('Get in inner_b') return func(*args, **kwargs) return inner_b @decorator_b @decorator_a def f(x): print ('Get in f') return x * 2 f(1)

輸出結果:

Get in decorator_a Get in decorator_b Get in inner_b Get in inner_a Get in f

運行分析:

上面代碼先定義兩個函數: decotator_a, decotator_b, 這兩個函數實現的功能是,接收一個函數作為參數,然後返回創建的另一個函數;

在這個創建的函數裡,調用接收的函數(文字比代碼繞人)。最後定義的函數 f 采用上面定義的decotator_a, decotator_b作為裝飾函數。

在當我們以1為參數,調用裝飾後的函數 f 後, decotator_a, decotator_b 的順序是什麼呢(這裡為了表示函數執行的先後順序,采用打印輸出的方式來查看函數的執行順序)?

如果不假思索,根據自下而上的原則來判斷地話,先執行 decorator_a 再執行 decorator_b , 那麼會先輸出 Get in decotator_a, Get in inner_a 再輸出 Get in decotator_b, Get in inner_b 。然而事實并非如此。

2.2函數和函數調用的區别

為什麼是先執行inner_b 再執行inner_a 呢?為了徹底看清上面的問題,得先分清兩個概念:函數和函數調用。

上面的例子中 f 稱之為函數, f(1) 稱之為函數調用,後者是對前者傳入參數進行求值的結果。在Python中函數也是一個對象,所以 函數f 是指代一個函數對象,它的值是函數本身, f(1) 是對函數的調用,它的值是調用的結果,這裡的定義下 f(1) 的值為2。同樣地,拿上面的decorator_a函數來說,它返回的是個函數對象inner_a ,這個函數對象是它内部定義的。在inner_a 裡調用了函數 func ,将 函數func 的調用結果作為值返回。

2.3裝飾器函數在被裝飾函數定義好後立即執行

當裝飾器裝飾一個函數時,究竟發生了什麼。現在簡化我們的例子,假設是下面這樣的:

def decorator_a(func): print ('Get in decorator_a') def inner_a(*args, **kwargs): print ('Get in inner_a') return func(*args, **kwargs) return inner_a @decorator_a def f(x): print ('Get in f') return x * 2

解讀:

@decorator_a #等同于調用函數的時候上方加上:f=decorator_a(f) def f(x): print ('Get in f') return x * 2 # 相當于 def f(x): print ('Get in f') return x * 2 f = decorator_a(f)

運行分析:

當解釋器執行這段代碼時, decorator_a 已經調用了,它把函數名f 作為參數傳遞給形參fun, 返回它内部生成的一個函數inner_a,所以此後 f 指代的是 decorater_a 裡面返回的 inner_a。所以當以後調用 函數f 時,實際上相當于調用 inner_a,傳給函數 f 的參數會傳給函數inner_a, 在調用函數inner_a 時,會把接收到的參數(函數名 f)傳給inner_a裡的 形參func=f ,最後返回的是 調用函數f 的值,所以在最外面看起來就像是直接調用函數 f 一樣。

2.4案例執行順序分析

當理清上面兩方面概念時,就可以清楚地看清最原始的例子中發生了什麼。當解釋器執行下面這段代碼時,實際上按照從下到上的順序已經依次調用了 decorator_a和 decorator_b,這是會輸出對應的 Get in decorator_a 和 Get in decorator_b。 這時候函數f 已經相當于 函數decorator_b裡的 函數inner_b。但因為 函數 f 并沒有被調用,所以 函數inner_b 并沒有被調用,依次類推 函數inner_b内部的 函數inner_a也沒有被調用,所以 Get in inner_a 和 Get in inner_b也不會被輸出。

案例:

def decorator_a(func): print ('Get in decorator_a') def inner_a(*args, **kwargs): print ('Get in inner_a') return func(*args, **kwargs) return inner_a def decorator_b(func): print ('Get in decorator_b') def inner_b(*args, **kwargs): print ('Get in inner_b') return func(*args, **kwargs) return inner_b @decorator_b @decorator_a def f(x): print ('Get in f') return x * 2 f(1)

輸出結果:

Get in decorator_a Get in decorator_b Get in inner_b Get in inner_a Get in f

然後第21行,當我們對函數 f 傳入參數1進行調用時,函數inner_b被調用了,它會先打印 Get in inner_b,然後在函數 inner_b 内部調用了函數inner_a,所以會再打印 Get in inner_a, 然後在函數 inner_a 内部調用的原來的函數 f, 并且将結果作為最終的返回值。這時候你該知道為什麼輸出結果會是那樣,以及對裝飾器執行順序實際發生了什麼有一定了解了吧。

2.5案例執行順序總結

-多個裝飾器執行的順序,是按照從下到上的順序執行裝飾器,再執行函數本身。

-裝飾器的外函數和内函數之間的語句是沒有裝飾到目标函數上的,而是在裝載裝飾器時的附加操作。

15~19行是裝載裝飾器的過程,相當于執行了f=decorator_b(decorator_a(f)):

  1. 此時先執行decorator_a(f),結果是輸出 Get in decorator_a,将形參func指向函數f、并返回函數inner_a;
  2. 然後執行函數 decorator_b(inner_a),結果是輸出 Get in decorator_b,将形參 func指向函數inner_a、并返回函數inner_b;
  3. 函數 f本身相當于函數inner_b。

21行則是實際調用被裝載的函數,用函數名 inner_b替代了函數名f。

  1. 這時實際上執行的是函數inner_b;
  2. 運行到函數func()時執行函數inner_a;
  3. 再運行到函數 func()時執行未修飾的函數f。
四、裝飾器傳參

案例:傳遞2個參數

def w1(fn): """裝飾器函數""" print("---正在裝飾---") def inner(a, b): # 如果a, b沒有定義,那麼會導緻19行代碼調用失敗 print("---正在驗證---") fn(a, b) # 如果沒有把a, b當實參進行傳遞,那麼會導緻調用13行的函數失敗 return inner @w1 def f1(a, b): """業務函數""" print(a b) f1(10, 20)

輸出結果:

---正在裝飾--- ---正在驗證--- 30


案例:不定長參數

def w1(fn): """裝飾器函數""" print("---正在裝飾---") def inner(*args, **kwargs): # 如果a, b沒有定義,那麼會導緻19行代碼調用失敗 print("---正在驗證---") fn(*args, **kwargs) # 如果沒有把a, b當實參進行傳遞,那麼會導緻調用13行的函數失敗 return inner @w1 def f1(a, b): """業務函數""" print(a b) @w1 def f2(a, b, c): """業務函數""" print(a b c) f1(10, 20) f2(10, 20, 30)

輸出結果:

---正在裝飾--- ---正在裝飾--- ---正在驗證--- 30 ---正在驗證--- 60

五、裝飾器返回值

案例 :

def w1(fn): """裝飾器函數""" print("---正在裝飾---") def inner(): print("---正在驗證---") ret = fn() # 保存返回來的字符串 return ret # 把字符串返回到20行的調用處 return inner @w1 def test(): """業務函數""" print("---test---") return "這是原函數返回值" ret = test() # 需要用參數來接收返回值 print(ret)

輸出結果:

---正在裝飾--- ---正在驗證--- ---test--- 這是原函數返回值

六、通用裝飾器

案例:

def w1(fn): """裝飾器函數""" def inner(*args, **kwargs): print("---記錄日志---") ret = fn(*args, **kwargs) # 保存返回來的字符串 return ret # 把字符串返回到20行的調用處 return inner @w1 def test1(): """不帶返回值""" print("---test1---") @w1 def test2(): """帶返回值""" print("---test2---") return "這是原函數返回值" @w1 def test3(a): """業務函數""" print("---test3中的數據:%d---" % a) ret1 = test1() print(ret1) ret2 = test2() print(ret2) ret3 = test3(10) print(ret3)

輸出結果:

---記錄日志--- ---test1--- None ---記錄日志--- ---test2--- 這是原函數返回值 ---記錄日志--- ---test3中的數據:10--- None

七、裝飾器帶參數

案例:

def func_arg(arg): def func(funtionName): def func_in(): print("輸出給裝飾器傳入的參數:%s" % arg) if arg == "hello": funtionName() funtionName() else: funtionName() return func_in return func @func_arg("hello") def test(): print("---test---") @func_arg("haha") def test2(): print("---test2---") test() test2()

輸出結果:

輸出給裝飾器傳入的參數:hello ---test--- ---test--- 輸出給裝飾器傳入的

運行分析:

  1. 先執行func_arg("hello")函數,這個函數return的結果是func這個函數的引用;
  2. 執行@func;
  3. 使用@func對函數test進行裝飾
,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved