Python 装饰器

Jinku Hu 2023年1月30日 2018年7月22日
  1. 装饰器函数
  2. 装饰器类
  3. 装饰方法
Python 装饰器

Python 装饰器能够动态地改变函数、方法或类的功能,而不必直接使用子类或更改修饰函数的源代码。如果使用得当,装饰器可以成为开发过程中的强大工具。我们来介绍 Python 中装饰器函数的实现和应用。

装饰器函数

装饰器增强了其他函数或方法的功能,任何将函数作为参数并返回一个增强的函数的函数都可以用作装饰器

# 这是一个简单的装饰器的例子,但它没有增加被装饰函数的功能
def super_secret_function(f):
    return f

@super_secret_function
def my_function():
    print("This is my secret function.")

这里@是一个语法糖,它等同于以下内容:

my_function = super_secret_function(my_function)

为了理解装饰器的工作原理,记住以下这一点很重要。这个非语法糖语法清楚地说明了为什么装饰器函数将函数作为参数,以及为什么它应该返回另一个函数。下面我们来看一下,如果不返回函数将会发生什么:

def disabled(f):
    """
    This function returns nothing, and hence removes the decorated function
    from the local scope.
    """
    pass

@disabled
def my_function():
    print("This function can no longer be called...")

my_function()
# TypeError: 'NoneType' object is not callable

因此,我们通常在装饰器中定义一个新函数并返回它。这个新函数首先要做它需要做的事情,然后调用原始函数,最后处理返回值。看下下面这个简单的装饰器函数,它打印原始函数接收的参数,然后调用原始函数。

#This is the decorator
def print_args(func):
    def inner_func(*args, **kwargs):
        print(args)
        print(kwargs)
        return func(*args, **kwargs) #Call the original function with its arguments.
    return inner_func

@print_args
def multiply(num_a, num_b):
    return num_a * num_b
  
print(multiply(3, 5))
#Output:
# (3,5) - This is actually the 'args' that the function receives
# {} - This is the 'kwargs', empty because we didn't specify keyword arguments
# 15 - The result of the function

装饰器类

正如上面介绍的,装饰器是一个可以应用于另一个函数来增强其功能的函数。语法糖相当于以下内容:my_func = decorator(my_func)。但如果如果装饰器是一个类呢?语法仍然有效,除了 my_func 被替换为 decorator 类的实例。如果这个类已经实施了 __call__() 魔术方法,那么仍然可以像使用函数一样的来使用 my_func

class Decorator(object):
    """Simple decorator class."""

    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('Before the function call.')
        res = self.func(*args, **kwargs)
        print('After the function call.')
        return res

@Decorator
def testfunc():
    print('Inside the function.')

testfunc()
# Before the function call
# Inside the function
# After the function call

请注意,使用类装饰器来装饰的函数,其类型将不再被视为函数:

import types
isinstance(testfunc, types.FunctionType)
# False
type(testfunc)
# <class '__main__.Decorator'>

装饰方法

你需要定义一个额外的 __get__ 方法来装饰方法。

from types import MethodType

class Decorator(object):
    def __init__(self, func):
        self.func = func
        
    def __call__(self, *args, **kwargs):
        print('Inside the decorator.')
        return self.func(*args, **kwargs)
    
    def __get__(self, instance, cls):
        # Return a Method if it is called on an instance
        return self if instance is None else MethodType(self, instance)

class Test(object):
    @Decorator
    def __init__(self):
        pass
    
a = Test()
Inside the decorator.
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn