本篇文章為大家帶來了關於python的相關知識,其中主要介紹了關於裝飾器的相關問題,包括了閉包、裝飾器、使用多個裝飾器、帶參數的裝飾器等等內容,下面一起來看一下,希望對大家有幫助。
推薦學習:python影片教學
#一、閉包
要了解什麼是裝飾器(decorator),我們首先需要知道閉包(closure)的概念。
閉包,又稱閉包函數或閉合函數,通俗一點來講,當某個函數被當成物件返回時還夾帶了外部變量,就形成了一個閉包。
以列印Hello World為例,我們先來看看巢狀函數的結構應該是什麼樣的:
def print_msg(msg): def printer(): print(msg) printer()print_msg('Hello World')# Hello World
執行print_msg('Hello World')
相當於執行了printer()
,也就是執行print(msg)
,所以會輸出Hello World
。
我們再來看一下如果是閉包,該是什麼樣的結構:
def print_msg(msg): def printer(): print(msg) return printer my_msg = print_msg('Hello World')my_msg()# Hello World
本例中的
printer
函數就是閉包。
執行print_msg('Hello World')
實際上是傳回如下這樣一個函數,它夾帶了外部變數'Hello World'
:
def printer(): print('Hello World')
於是呼叫my_msg
就相當於執行printer()
。
那麼要如何判斷函數是否是閉包函數呢?閉包函數的 __closure__
屬性裡面定義了一個元組用於存放所有的cell對象,每個cell對象保存了這個閉包中所有的外部變數。而普通函數的 __closure__
屬性為 None
。
def outer(content): def inner(): print(content) return innerprint(outer.__closure__) # Noneinner = outer('Hello World')print(inner.__closure__) # (<cell>,)</cell>
由此可見 outer
函數不是閉包,而 inner
函數是閉包。
我們還可以查看閉包所攜帶的外部變數:
print(inner.__closure__[0].cell_contents)# Hello World
說了那麼多,那麼閉包究竟有什麼用呢?閉包存在的意義就是它夾帶了外部變數(私貨),如果它不夾帶私貨,那麼就和普通的函數沒有任何區別。
閉包的優點如下:
- 局部變數無法共享且長久的保存,而全域變數可能造成變數污染,閉包既可以長久的保存變數又不會造成全域污染。
- 閉包使得函數內局部變數的值始終保持在記憶體中,不會在外部函數呼叫後自動清除。
二、裝飾器
我們先考慮這樣一個場景,假設先前寫的一個函數已經實現了4個功能,為簡單起見,我們用print
語句來代表每一個具體的功能:
def module(): print('功能1') print('功能2') print('功能3') print('功能4')
現在,由於某種原因,你需要為module
這個函數新增一個功能5
,你完全可以這樣修改:
def module(): print('功能1') print('功能2') print('功能3') print('功能4') print('功能5')
但在現實業務中,直接做出這樣的修改往往是比較危險的(會變得不容易維護)。那麼如何在不修改原函數的基礎上去為它新添一個功能呢?
你可能已經想到了使用之前的閉包知識:
def func_5(original_module): def wrapper(): original_module() print('功能5') return wrapper
#func_5
代表該函數主要用於實作功能5
,我們接下來將module
傳入進去觀察效果:
new_module = func_5(module)new_module()# 功能1# 功能2# 功能3# 功能4# 功能5
可以看出,我們的新模組:new_module
已經實作了功能5
。
在上面的範例中,函數
func_5
就是一個裝飾器,它裝飾了原來的模組(為它新添了一個功能)。
#當然,Python有更簡潔的寫法(稱之為語法糖),我們可以將@符號與裝飾器函數的名稱一起使用,並將其放置在要裝飾的函數的定義上方:
def func_5(original_module): def wrapper(): original_module() print('功能5') return wrapper@func_5def module(): print('功能1') print('功能2') print('功能3') print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能5
基於此,我們可以在不修改原始函數的基礎上完成計時任務(計算原始函數的運行時間),如下:
def timer(func): def wrapper(): import time tic = time.time() func() toc = time.time() print('程序用时: {}s'.format(toc - tic)) return wrapper@timerdef make_list(): return [i * i for i in range(10**7)]my_list = make_list()# 程序用时: 0.8369960784912109s
事實上,my_list
並不是列表,直接列印會顯示None
,這是因為我們的wrapper
函數沒有設定回傳值。如果需要取得make_list
的回傳值,可以這樣修改wrapper
函數:
def wrapper(): import time tic = time.time() a = func() toc = time.time() print('程序用时: {}s'.format(toc - tic)) return a
三、使用多個裝飾器
假如我們要為module
新增功能5
和功能6
(以數字順序),那該如何做呢?
好在Python允許同時使用多個裝飾器:
def func_5(original_module): def wrapper(): original_module() print('功能5') return wrapperdef func_6(original_module): def wrapper(): original_module() print('功能6') return wrapper@func_6@func_5def module(): print('功能1') print('功能2') print('功能3') print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能5# 功能6
上述過程實際上等價於:
def module(): print('功能1') print('功能2') print('功能3') print('功能4')new_module = func_6(func_5(module))new_module()
此外,需要注意的是,在使用多個裝飾器時,最靠近函數定義的裝飾器會先裝飾該函數,如果我們改變裝飾順序,則輸出結果也會改變:
@func_5@func_6def module(): print('功能1') print('功能2') print('功能3') print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能6# 功能5
四、被裝飾的函數帶有參數
如果被裝飾的函數帶有參數,那該如何去建構裝飾器呢?
考慮這樣一個函數:
def pide(a, b): return a / b
当b=0 时会出现 ZeropisionError
。如何在避免修改该函数的基础上给出一个更加人性化的提醒呢?
因为我们的 pide
函数接收两个参数,所以我们的 wrapper
函数也应当接收两个参数:
def smart_pide(func): def wrapper(a, b): if b == 0: return '被除数不能为0!' else: return func(a, b) return wrapper
使用该装饰器进行装饰:
@smart_pidedef pide(a, b): return a / bprint(pide(3, 0))# 被除数不能为0!print(pide(3, 1))# 3.0
如果不知道要被装饰的函数有多少个参数,我们可以使用下面更为通用的模板:
def decorator(func): def wrapper(*args, **kwargs): # ... res = func(*args, **kwargs) # ... return res # 也可以不return return wrapper
五、带参数的装饰器
我们之前提到的装饰器都没有带参数,即语法糖 @decorator
中没有参数,那么该如何写一个带参数的装饰器呢?
前面实现的装饰器都是两层嵌套函数,而带参数的装饰器是一个三层嵌套函数。
考虑这样一个场景。假如我们在为 module
添加新功能时,希望能够加上实现该功能的开发人员的花名,则可以这样构造装饰器(以 功能5
为例):
def func_5_with_name(name=None): def func_5(original_module): def wrapper(): original_module() print('功能5由{}实现'.format(name)) return wrapper return func_5
效果如下:
@func_5_with_name(name='若水')def module(): print('功能1') print('功能2') print('功能3') print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能5由若水实现
对于这种三层嵌套函数,我们可以这样理解:当为 func_5_with_name
指定了参数后,func_5_with_name(name='若水')
实际上返回了一个 decorator
,于是 @func_5_with_name(name='若水')
就相当于 @decorator
。
六、使用类作为装饰器
将类作为装饰器,我们需要实现 __init__
方法和 __call__
方法。
以计时器为例,具体实现如下:
class Timer: def __init__(self, func): self.func = func def __call__(self): import time tic = time.time() self.func() toc = time.time() print('用时: {}s'.format(toc - tic))@Timerdef make_list(): return [i**2 for i in range(10**7)]make_list()# 用时: 2.928966999053955s
如果想要自定义生成列表的长度并获得列表(即被装饰的函数带有参数情形),我们就需要在 __call__
方法中传入相应的参数,具体如下:
class Timer: def __init__(self, func): self.func = func def __call__(self, num): import time tic = time.time() res = self.func(num) toc = time.time() print('用时: {}s'.format(toc - tic)) return res@Timerdef make_list(num): return [i**2 for i in range(num)]my_list = make_list(10**7)# 用时: 2.8219943046569824sprint(len(my_list))# 10000000
如果要构建带参数的类装饰器,则不能把 func
传入 __init__
中,而是传入到 __call__
中,同时 __init__
用来初始化类装饰器的参数。
接下来我们使用类装饰器来复现第五章节中的效果:
class Func_5: def __init__(self, name=None): self.name = name def __call__(self, func): def wrapper(): func() print('功能5由{}实现'.format(self.name)) return wrapper@Func_5('若水')def module(): print('功能1') print('功能2') print('功能3') print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能5由若水实现
七、内置装饰器
Python中有许多内置装饰器,这里仅介绍最常见的三种:@classmethod
、@staticmethod
和 @property
。
7.1 @classmethod
@classmethod
用于装饰类中的函数,使用它装饰的函数不需要进行实例化也可调用。需要注意的是,被装饰的函数不需要 self
参数,但第一个参数需要是表示自身类的 cls
参数,它可以来调用类的属性,类的方法,实例化对象等。
cls
代表类本身,self
代表实例本身。
具体请看下例:
class A: num = 100 def func1(self): print('功能1') @classmethod def func2(cls): print('功能2') print(cls.num) cls().func1()A.func2()# 功能2# 100# 功能1
7.2 @staticmethod
@staticmethod
同样用来修饰类中的方法,使用它装饰的函数的参数没有任何限制(即无需传入 self
参数),并且可以不用实例化调用该方法。当然,实例化后调用该方法也是允许的。
具体如下:
class A: @staticmethod def add(a, b): return a + bprint(A.add(2, 3))# 5print(A().add(2, 3))# 5
7.3 @property
使用 @property
装饰器,我们可以直接通过方法名来访问类方法,不需要在方法名后添加一对 ()
小括号。
class A: @property def printer(self): print('Hello World')a = A()a.printer# Hello World
除此之外,@property
还可以用来防止类的属性被修改。考虑如下场景
class A: def __init__(self): self.name = 'ABC'a = A()print(a.name)# ABCa.name = 1print(a.name)# 1
可以看出类中的属性 name
可以被随意修改。如果要防止修改,则可以这样做
class A: def __init__(self): self.name_ = 'ABC' @property def name(self): return self.name_ a = A()print(a.name)# ABCa.name = 1print(a.name)# AttributeError: can't set attribute
推荐学习:python视频教程
以上是歸納總結Python中的裝飾器知識點的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python更易學且易用,C 則更強大但複雜。 1.Python語法簡潔,適合初學者,動態類型和自動內存管理使其易用,但可能導致運行時錯誤。 2.C 提供低級控制和高級特性,適合高性能應用,但學習門檻高,需手動管理內存和類型安全。

Python和C 在内存管理和控制方面的差异显著。1.Python使用自动内存管理,基于引用计数和垃圾回收,简化了程序员的工作。2.C 则要求手动管理内存,提供更多控制权但增加了复杂性和出错风险。选择哪种语言应基于项目需求和团队技术栈。

Python在科學計算中的應用包括數據分析、機器學習、數值模擬和可視化。 1.Numpy提供高效的多維數組和數學函數。 2.SciPy擴展Numpy功能,提供優化和線性代數工具。 3.Pandas用於數據處理和分析。 4.Matplotlib用於生成各種圖表和可視化結果。

選擇Python還是C 取決於項目需求:1)Python適合快速開發、數據科學和腳本編寫,因其簡潔語法和豐富庫;2)C 適用於需要高性能和底層控制的場景,如係統編程和遊戲開發,因其編譯型和手動內存管理。

Python在數據科學和機器學習中的應用廣泛,主要依賴於其簡潔性和強大的庫生態系統。 1)Pandas用於數據處理和分析,2)Numpy提供高效的數值計算,3)Scikit-learn用於機器學習模型構建和優化,這些庫讓Python成為數據科學和機器學習的理想工具。

每天學習Python兩個小時是否足夠?這取決於你的目標和學習方法。 1)制定清晰的學習計劃,2)選擇合適的學習資源和方法,3)動手實踐和復習鞏固,可以在這段時間內逐步掌握Python的基本知識和高級功能。

Python在Web開發中的關鍵應用包括使用Django和Flask框架、API開發、數據分析與可視化、機器學習與AI、以及性能優化。 1.Django和Flask框架:Django適合快速開發複雜應用,Flask適用於小型或高度自定義項目。 2.API開發:使用Flask或DjangoRESTFramework構建RESTfulAPI。 3.數據分析與可視化:利用Python處理數據並通過Web界面展示。 4.機器學習與AI:Python用於構建智能Web應用。 5.性能優化:通過異步編程、緩存和代碼優

Python在開發效率上優於C ,但C 在執行性能上更高。 1.Python的簡潔語法和豐富庫提高開發效率。 2.C 的編譯型特性和硬件控制提升執行性能。選擇時需根據項目需求權衡開發速度與執行效率。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3漢化版
中文版,非常好用

Atom編輯器mac版下載
最受歡迎的的開源編輯器

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)