python的裝飾器和閉包

Gold_stein發表於2024-10-20

python的裝飾器和閉包

內容

Python 的裝飾器是什麼?

裝飾器(Decorator)是 Python 中的一種用於擴充套件或修改函式/類行為的設計模式。它允許在不修改原有函式或類的情況下,動態地為其增加新的功能。這種功能在實踐中非常有用,例如在日誌記錄、效能測試、許可權控制、快取等場景中。


裝飾器的定義與使用

  1. 裝飾器的基本結構是一個函式,它接收另一個函式作為引數,並返回一個新的函式。
  2. 使用裝飾器時,通常透過 @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!
呼叫函式後的操作

解釋:

  1. @my_decorator 裝飾器將 say_hello() 函式傳遞給 my_decorator()
  2. my_decorator 返回一個新的函式 wrapper,該函式包裝了原始的 say_hello()
  3. 當呼叫 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 先執行。

裝飾器的實際應用場景:

  1. 日誌記錄:
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!

  1. 許可權控制:
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)是指在內嵌函式中,引用了外部函式中的變數,並且即使外部函式執行完畢,這些變數仍然儲存在內嵌函式的作用域中。這種結構使得函式能夠“記住”其外部環境的變數。

閉包的核心在於:

  1. 函式巢狀:函式內部定義另一個函式。
  2. 外層函式的變數被內層函式引用
  3. 外層函式的作用域結束後,變數依然儲存在內層函式中

閉包的簡單例子

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

解釋:

  1. outer_function 接受一個引數 x,並定義了一個內嵌函式 inner_function
  2. inner_function 使用了外部函式 outer_function 中的變數 x
  3. outer_function 執行完畢後,返回 inner_function 函式物件。
  4. 雖然 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)是函式的一種特性,使得內嵌函式可以“記住”外部函式的變數,即使外部函式已經執行完畢。這種特性使得閉包在程式設計中非常有用,尤其是在處理狀態儲存、裝飾器和回撥函式時。

關鍵點:

  1. 內嵌函式引用外部函式中的變數。
  2. 外部函式執行完畢後,內嵌函式仍然可以使用這些變數。
  3. nonlocal 關鍵字允許內嵌函式修改外部作用域中的變數。

閉包不僅使程式碼更加簡潔,還能實現強大的功能,是 Python 程式設計中的重要概念。

相關文章