Python裝飾器

1、裝飾器的本質

裝飾器本質上是一個閉包函數,可以讓其他函數在不需要任何程式碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數。

  • 閉包函數

    1)必須是嵌套函數,即外函數中定義了一個內函數;

    2)內函數引用了外函數作用域中(注意:非全局)的變數;

    3)外函數的返回值是內函數的引用;

    4)檢測函數是否為閉包函數:function.__closure__,是閉包,返回一個cell,不是閉包,返回None

     

  • 函數

    1)什麼是函數:組織好的、可重複使用的,用來實現單一或相關聯功能的程式碼段;

    2)函數的優點:函數能提高應用的模組性和程式碼的重複利用率;

    3)函數的定義:使用def關鍵字;

    def func():
        print("Hello, World!")
    

     

    4)函數的參數:根據函數在定義時和調用時,函數的參數有不同的概念;

    • 定義時:位置參數、默認參數、不定長參數;
    def func(a, b=0, *args, **kwargs):
        print(f"位置參數:{a}")
        print(f"默認參數:{b}")
        print(f"不定長參數:元組{args},字典{kwargs}")
    
    • 調用時:必備參數(必須傳)、關鍵字參數(可傳可不傳)、不定長參數(可傳可不傳);
    func("Hello")                            # "Hello" >> a
    func("Hello", b=1)                    # "Hello" >> a, 1 >> b
    func("Hello", 2)                        # "Hello" >> a, 2 >> b
    func("Hello", 2, 3, c="World")  # "Hello" >> a, 2 >> b, 3 >> *args, c="World" >> **kwargs
    

     

    5)匿名函數:使用lambda關鍵字,只是一個表達式,不是程式碼塊,邏輯有限;

    add = lambda a, b: a + b
    

     

    6)變數作用域:LEGB;

    • L:Local,局部作用域;
    • E:Enclosing,閉包函數外的函數中;
    • G:Global,全局作用域;
    • B:Built-in,內建作用域;

     

    7)有關函數的幾個概念;

    • 函數即變數:函數可作為參數,也可作為返回值;
    • 高階函數:以函數作為參數或返回值的函數,內置函數map()filter()functools包中的reduce()
    • 嵌套函數:函數里定義函數;
    • 嵌套函數中變數的生命周期:正常情況下,一個函數運行結束,函數所有局部對象都會被回收,釋放記憶體。但是閉包是一種特殊情況。閉包中,如果外函數在結束時,外函數作用域中的臨時變數被內函數調用,外函數就會將該變數綁定給內函數,然後再結束;

 

2、裝飾器應滿足條件

  • 給被裝飾函數添加新的功能;
  • 不能改變被裝飾函數的程式碼;
  • 不能改變被裝飾函數的調用方式;

 

3、裝飾器的應用場景

  • 插入日誌;
  • 性能測試;
  • 事務處理;
  • 快取、許可權校驗;

 

4、裝飾器的固定格式

  • 格式1

    def decorator(func):                          # decorator函數中嵌套了inner函數
        def inner(*args, **kwargs):
            """something before func"""
            f = func(*args, **kwargs)          # 內函數inner調用了外函數decorator作用域中的變數func
            """something after func"""
            return f
    
        return inner                                  # 外函數decorator的返回值是inner(對內函數inner的引用)
    
  • 格式2

    from functools import wraps
    
    
    def decorator(func):
        @wraps
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
    
        return wrapper
    

 

5、裝飾器的演進

  • 將被裝飾的函數顯式傳入裝飾器,被裝飾函數沒有參數和返回值;

    import time
    
    
    def func():
        print("I'm func.")
    
    
    def timer(f):
        def inner():
            start = time.time()
            f()
            end = time.time()
            print(end - start)
    
        return inner
    
    
    func = timer(func)          # 將函數func作為參數傳遞給timer裝飾器,將返回結果賦值給變數func
    func()                             # func(),即執行了func
    

     

  • 將被裝飾函數放在語法糖下一行,被裝飾函數不帶參數,沒有返回值;

    import time
    
    
    def timer(f):
        def inner():
            start = time.time()
            f()
            end = time.time()
            print(end - start)
    
        return inner
    
    
    @timer                    # @timer被稱為語法糖,等價於func=timer(func)
    def func():
        print("I'm func.")
    
    
    func()
    

     

  • 將被裝飾函數放在語法糖下一行,被裝飾函數帶1個參數,沒有返回值;

    import time
    
    
    def timer(f):
        def inner(i):
            start = time.time()
            f(i)
            end = time.time()
            print(end - start)
    
        return inner
    
    
    @timer
    def func(x):
        print(x)
    
    
    func("Hello, World!")
    

     

  • 將被裝飾函數放在語法糖下一行,被裝飾函數帶2個參數,沒有返回值;

    import time
    
    
    def timer(f):
        def inner(*args, **kwargs):
            start = time.time()
            f(*args, **kwargs)
            end = time.time()
            print(end - start)
    
        return inner
    
    
    @timer
    def func(x, y):
        print(f"The first argument is {x}, the second is {y}.")
    
    
    func(2, 24)
    

     

  • 將被裝飾函數放在語法糖下一行,被裝飾函數帶多個參數,有返回值;

    import time
    
    
    def timer(f):
        def inner(*args, **kwargs):
            start = time.time()
            r = f(*args, **kwargs)
            end = time.time()
            print(end - start)
            return r
    
        return inner
    
    
    @timer
    def func(x, y, z=3):
        print(f"Arguments are {}、{} and {}.")
        return 'Over'
    
    
    func(1, 2, z=3)
    print(func(1, 2, z=3))
    

     

  • 帶參數的裝飾器,被裝飾函數不帶參數,沒有返回值;

    def outer(flag):
        def decorator(f):
            def inner(*args, **kwargs):
                if flag:
                    print("Ahead func")
                r = f(*args, **kwargs)
                if flag:
                    print("After func")
                return r
    
            return inner
    
        return decorator
    
    
    @outer(True)
    def func():
      print("Hello, World!")
    
    
    func()
    

     

  • 多個裝飾器裝飾同一個函數,被裝飾函數不帶參數,沒有返回值;

    def decorator1(f):
        def inner():
            print('Decorator1, before f')
            f()
            print('Decorator1, after f')
    
        return inner
    
    def decorator2(f):
        def inner():
            print('Decorator2, before f')
            f()
            print('Decorator2, after f')
    
        return inner
    
    
    @decorator2
    @decorator1
    def func():
        print("I'm func.")
    

 

6、裝飾器的總結

Python中的裝飾器一共有4種類型:函數裝飾函數、函數裝飾類、類裝飾函數、類裝飾類;

  • 函數裝飾函數:函數作為參數被傳入裝飾器函數中;

    def decorator(f):
        def inner(*args, **kwargs):
            print(f"function name: {f.__name__}")
            r = f(*args, **kwargs)
            return r
        return inner
    
    
    @decorator                    # @decorator 等價於 addition=decorator(addition)
    def addition(x, y):
        return x + y
    
    
    print(addition(3, 7))
    

     

  • 函數裝飾類:類作為參數被傳入裝飾器函數中;

    def decorator(cls):
        def inner(*args, **kwargs):
            print(f"class name: {cls.__name__}")
            return cls(*args, **kwargs)
        return inner
    
    
    @decorator                     # @decorator 等價於 Func=decorator(Func)
    class Func:
        def __init__(self, item):
            self.item = item
    
        def func(self):
            print(f"self.a = {self.a}")
    
    
    f = Func('Hello, World!')
    f.func()
    

     

  • 類裝飾函數:函數作為參數被傳入裝飾器類中;

    class Decorator:
        def __init__(self, f):
            self.f = f
    
        def __call__(self, item):
            print(f"function name: {self.f.__name__}")
            return self.f(item)
    
    
    @Decorator                    # @Decorator 等價於 func = Decorator(func).__call__
    def func(i):
        return i
    
    
    print(func("Hello, World!"))
    

     

  • 類裝飾類:類作為參數被傳入裝飾器類中;

    class Decorator:
        def __init__(self, cls):
            self.cls = cls
    
        def __call__(self, item):
            print(f"class name: {self.cls.__name__}")
            return self.cls(item)
    
    
    @Decorator                    # @Decorator 等價於 Func = Decorator(Func).__call__
    class Func:
        def __init__(self, v):
            self.v = v
    
        def func(self):
            print(self.v)
    
    
    f = Func("Hello, World!")
    f.func()