一文搞懂Python装饰器

2023-11-30 19:30:39 浏览数 (2)

装饰器是一种非常有用和强大的python特性,它可以让你在不修改原函数的情况下,给函数添加一些额外的功能。在这篇文章中,我将介绍装饰器的概念、语法、作用和实例。

装饰器的概念

装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。这个新的函数在调用原函数之前或之后,执行一些额外的操作,从而增强或修改原函数的功能。例如,下面的代码定义了一个简单的装饰器hello,它在调用原函数之前打印一句问候语:

代码语言:javascript复制
def hello(func):
    # 定义一个新的函数
    def wrapper():
        # 在调用原函数之前打印一句问候语
        print("Hello, this is a decorator.")
        # 调用原函数
        func()
    # 返回新的函数
    return wrapper


#定义一个普通的函数


def foo():
    print("This is a normal function.")


# 使用装饰器修饰普通函数
foo = hello(foo)

# 调用修饰后的函数
foo()
输出结果为:

Hello, this is a decorator.
This is a normal function.

可以看到,修饰后的函数foo在执行原来的功能之前,多了一句问候语。这就是装饰器的基本概念。

装饰器的语法

python提供了一种简洁和优雅的语法来使用装饰器,就是使用@符号。只需要在定义函数之前,加上@装饰器名,就可以实现对函数的修饰。例如,上面的代码可以改写为:

代码语言:javascript复制
def hello(func):
    def wrapper():
        print("Hello, this is a decorator.")
        func()
    return wrapper

# 使用@符号修饰普通函数
@hello
def foo():
    print("This is a normal function.")

# 调用修饰后的函数
foo()
输出结果和之前一样:

Hello, this is a decorator.
This is a normal function.

这种语法可以让代码更简洁和清晰,也可以避免对原函数名进行重复赋值。

装饰器的作用

装饰器可以用来实现很多有用和有趣的功能,例如:

日志:装饰器可以用来记录函数的调用情况,例如参数、返回值、执行时间等,方便进行调试和分析。例如,下面的代码定义了一个装饰器log,它可以打印出被修饰函数的名字、参数和返回值:
代码语言:javascript复制
import functools

def log(func):
    # 使用functools.wraps保留原函数的元信息
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # 打印被修饰函数的名字和参数
        print(f"Calling {func.__name__} with {args} and {kwargs}")
        # 调用原函数,并获取返回值
        result = func(*args, **kwargs)
        # 打印被修饰函数的返回值
        print(f"{func.__name__} returned {result}")
        # 返回原函数的返回值
        return result
    # 返回新的函数
    return wrapper

# 使用装饰器修饰一个求和函数
@log
def add(x, y):
    return x   y

# 调用修饰后的求和函数
add(1, 2)
输出结果为:

Calling add with (1, 2) and {}
add returned 3
缓存:装饰器可以用来缓存函数的返回值,避免重复计算,提高性能。例如,下面的代码定义了一个装饰器cache,它可以缓存一个斐波那契数列函数的返回值:
代码语言:javascript复制
def cache(func):
    # 创建一个字典,用来存储函数的参数和返回值
    cache_dict = {}
    # 定义一个新的函数,用来替代原函数
    def wrapper(n):
        # 如果参数在缓存中存在,直接返回缓存的值
        if n in cache_dict:
            return cache_dict[n]
        # 否则,调用原函数,并将返回值存入缓存
        else:
            result = func(n)
            cache_dict[n] = result
            return result
    # 返回新的函数
    return wrapper

# 使用装饰器修饰一个斐波那契数列函数
@cache
def fib(n):
    if n < 2:
        return n
    else:
        return fib(n-1)   fib(n-2)

# 测试装饰器的效果
print(fib(10)) # 输出55
print(fib(20)) # 输出6765
权限:装饰器可以用来检查函数的调用权限,例如用户身份、密码、角色等,实现安全和控制。例如,下面的代码定义了一个装饰器login_required,它可以要求用户输入密码才能调用被修饰的函数:
代码语言:javascript复制
def login_required(func):
    # 定义一个新的函数,用来替代原函数
    def wrapper():
        # 获取用户输入的密码
        password = input("Please enter your password: ")
        # 如果密码正确,调用原函数
        if password == "123456":
            func()
        # 否则,打印错误信息
        else:
            print("Wrong password!")
    # 返回新的函数
    return wrapper

# 使用装饰器修饰一个敏感的函数
@login_required
def secret():
    print("This is a secret message.")

# 调用修饰后的敏感函数
secret()
输出结果为:

Please enter your password: 123456
This is a secret message.

标题装饰器的实例

python标准库中提供了一些内置的装饰器,例如@staticmethod、@classmethod、@property等,它们可以用来改变类中方法的行为。例如:

@staticmethod:可以将一个类中的方法变成静态方法,即不需要传入实例或类作为第一个参数,可以直接通过类名或实例名调用。静态方法通常用来实现一些与类或实例无关的功能。例如:
代码语言:javascript复制
class Math:
    # 定义一个静态方法,用来计算两个数的最大公约数
    @staticmethod
    def gcd(a, b):
        if b == 0:
            return a
        else:
            return Math.gcd(b, a % b)

# 调用静态方法,不需要创建类的实例或传入类名
print(Math.gcd(12, 18)) # 输出6
@classmethod:可以将一个类中的方法变成类方法,即需要传入类作为第一个参数,通常命名为cls。类方法通常用来实现一些与类相关的功能,例如创建类的实例、修改类的属性等。例如:
代码语言:javascript复制
class Person:
    # 定义一个类属性,记录人类的数量
    count = 0

    def __init__(self, name):
        self.name = name
        # 每创建一个人类实例,数量加1
        Person.count  = 1

    # 定义一个类方法,用来打印人类的数量和信息
    @classmethod
    def show(cls):
        print(f"There are {cls.count} people.")
        print(f"They are {cls.__name__}.")

# 创建两个人类实例
p1 = Person("Alice")
p2 = Person("Bob")
# 调用类方法,不需要传入实例名
Person.show()


输出结果为:

There are 2 people.
They are Person.
@property`:可以将一个类中的方法变成属性,即可以通过实例名.方法名的方式访问,而不需要加括号。属性通常用来实现一些与实例状态相关的功能,例如获取或设置实例的属性、计算实例的属性等。例如:
代码语言:javascript复制
class Circle:
    def __init__(self, radius):
        self.radius = radius # 定义一个实例属性,表示圆的半径

    # 定义一个属性,表示圆的面积
    @property
    def area(self):
        return 3.14 * self.radius ** 2 # 返回圆的面积

# 创建一个圆类的实例
c = Circle(10)
# 调用属性,不需要加括号
print(c.area) # 输出314.0

以上就是我为你写的讲解python装饰器的文章,希望对你有帮助。

0 人点赞