python的裝飾器和閉包
內容
Python 的裝飾器是什麼?
裝飾器(Decorator)是 Python 中的一種用於擴充套件或修改函式/類行為的設計模式。它允許在不修改原有函式或類的情況下,動態地為其增加新的功能。這種功能在實踐中非常有用,例如在日誌記錄、效能測試、許可權控制、快取等場景中。
裝飾器的定義與使用
- 裝飾器的基本結構是一個函式,它接收另一個函式作為引數,並返回一個新的函式。
- 使用裝飾器時,通常透過
@decorator_name
的語法,將其作用於某個函式或方法。
簡單示例:
def my_decorator(func):
def wrapper():
print("呼叫函式前的操作")
func()
print("呼叫函式後的操作")
return wrapper
@my_decorator # 等同於 say_hello = my_decorator(say_hello)
def say_hello():
print("Hello, World!")
say_hello()
輸出:
呼叫函式前的操作
Hello, World!
呼叫函式後的操作
解釋:
@my_decorator
裝飾器將say_hello()
函式傳遞給my_decorator()
。my_decorator
返回一個新的函式wrapper
,該函式包裝了原始的say_hello()
。- 當呼叫
say_hello()
時,實際執行的是wrapper()
,從而在呼叫原函式前後新增了新的行為。
帶引數的裝飾器:
如果被裝飾的函式接受引數,那麼 wrapper
函式也需要接受相應的引數:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("呼叫函式前的操作")
result = func(*args, **kwargs)
print("呼叫函式後的操作")
return result
return wrapper
@my_decorator
def add(a, b):
print(f"計算 {a} + {b}")
return a + b
result = add(3, 5)
print(f"結果是: {result}")
輸出:
呼叫函式前的操作
計算 3 + 5
呼叫函式後的操作
結果是: 8
解釋:
- 使用
*args
和**kwargs
,確保裝飾器可以支援任意引數的函式。
多個裝飾器的使用:
多個裝飾器可以層層疊加,執行順序是從上到下應用裝飾器:
def decorator1(func):
def wrapper():
print("執行 decorator1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("執行 decorator2")
func()
return wrapper
@decorator1
@decorator2
def say_hi():
print("Hi!")
say_hi()
輸出:
執行 decorator1
執行 decorator2
Hi!
解釋:
@decorator1
包裹了@decorator2
,所以decorator1
先執行。
裝飾器的實際應用場景:
- 日誌記錄:
def log(func):
def wrapper(*args, **kwargs):
print(f"正在呼叫 {func.__name__} 函式")
return func(*args, **kwargs)
return wrapper
@log
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
輸出:
正在呼叫 greet 函式
Hello, Alice!
- 許可權控制:
def require_permission(permission):
def decorator(func):
def wrapper(*args, **kwargs):
if permission != "admin":
print("許可權不足,無法執行該操作")
return
return func(*args, **kwargs)
return wrapper
return decorator
@require_permission("user")
def delete_user():
print("使用者已刪除")
delete_user()
輸出:
許可權不足,無法執行該操作
類裝飾器:
除了裝飾函式之外,裝飾器還可以用於裝飾類:
def add_method(cls):
cls.new_method = lambda self: print("這是新新增的方法")
return cls
@add_method
class MyClass:
pass
obj = MyClass()
obj.new_method() # 呼叫裝飾器新增的新方法
輸出:
這是新新增的方法
總結:
- 裝飾器是一種強大的工具,可以動態地為函式或類新增功能。
- 它們透過
@decorator
的語法,使程式碼更加簡潔和易讀。 - 常見的應用場景包括:日誌記錄、許可權控制、快取、效能監控等。
理解裝飾器的關鍵在於:裝飾器本質上是高階函式,它們接收一個函式或類並返回一個新的函式或類。裝飾器的使用可以大大提高程式碼的複用性和可維護性。
什麼是閉包?
閉包(Closure)是指在內嵌函式中,引用了外部函式中的變數,並且即使外部函式執行完畢,這些變數仍然儲存在內嵌函式的作用域中。這種結構使得函式能夠“記住”其外部環境的變數。
閉包的核心在於:
- 函式巢狀:函式內部定義另一個函式。
- 外層函式的變數被內層函式引用。
- 外層函式的作用域結束後,變數依然儲存在內層函式中。
閉包的簡單例子
def outer_function(x):
def inner_function(y):
return x + y # 內嵌函式引用了外部函式的變數 x
return inner_function # 返回內嵌函式
closure = outer_function(10) # outer_function 執行完畢,返回 inner_function
result = closure(5) # 相當於呼叫 inner_function(5)
print(result) # 輸出: 15
解釋:
outer_function
接受一個引數x
,並定義了一個內嵌函式inner_function
。inner_function
使用了外部函式outer_function
中的變數x
。- 當
outer_function
執行完畢後,返回inner_function
函式物件。 - 雖然
outer_function
的作用域已經結束,但由於inner_function
是閉包,它保留了對變數x
的引用。
閉包的作用
- 儲存狀態:閉包可以記住函式的某些狀態,即使外部作用域已經結束。
- 延遲執行:閉包可以延遲計算或操作,直到真正需要時才執行。
- 函數語言程式設計的基礎:在某些語言中(如 Python、JavaScript),閉包是實現回撥、裝飾器等模式的重要基礎。
閉包與作用域的關係
讓我們再看一個涉及作用域的閉包示例:
def counter():
count = 0 # 外部變數
def increment():
nonlocal count # 使用 nonlocal 宣告引用外層作用域中的變數
count += 1
return count
return increment
counter_fn = counter() # 返回內嵌的 increment 函式
print(counter_fn()) # 輸出: 1
print(counter_fn()) # 輸出: 2
print(counter_fn()) # 輸出: 3
解釋:
counter
函式建立了一個變數count
,並返回一個increment
內嵌函式。- 每次呼叫
increment
時,都會更新並返回count
的值。 - 由於
increment
是閉包,它可以訪問外部函式的變數count
,即使counter
已經執行完畢。
閉包的常見應用:
1. 裝飾器:
裝飾器本質上就是閉包,封裝了原函式及其狀態。
def multiply_by(factor):
def decorator(func):
def wrapper(*args, **kwargs):
return factor * func(*args, **kwargs)
return wrapper
return decorator
@multiply_by(2)
def add(a, b):
return a + b
print(add(3, 5)) # 輸出: 16
解釋:
- 裝飾器
multiply_by(2)
建立了一個閉包,將factor=2
存在wrapper
中。 - 當呼叫
add(3, 5)
時,裝飾器將結果乘以2
。
2. 回撥函式:
閉包常用於非同步程式設計中的回撥函式,儲存狀態直到非同步操作完成。
總結:
閉包(Closure)是函式的一種特性,使得內嵌函式可以“記住”外部函式的變數,即使外部函式已經執行完畢。這種特性使得閉包在程式設計中非常有用,尤其是在處理狀態儲存、裝飾器和回撥函式時。
關鍵點:
- 內嵌函式引用外部函式中的變數。
- 外部函式執行完畢後,內嵌函式仍然可以使用這些變數。
nonlocal
關鍵字允許內嵌函式修改外部作用域中的變數。
閉包不僅使程式碼更加簡潔,還能實現強大的功能,是 Python 程式設計中的重要概念。