如何理解使用Python的装饰器

首先Python 的装饰器值是一种语法糖,用于动态修改函数或类的行为

大白话就是Python的装饰器只是Python语法指令的简单写法,他是用来修饰类和函数实现一些辅助功能,不使用装饰器也可以,可能实现起来比较麻烦(优雅是Python追求的特点之一)!

Python装饰器的基本原理:一个函数可以接受另一个函数作为参数,然后返回一个新的函数

一个引例

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

@log
def my_func(*args, **kw):
    print('hello world')

在这个例子中定义了一个装饰器 log,它接受一个函数作为参数,然后返回一个新的函数 wrapper。wrapper 函数中输出了一段日志,并调用原来的函数 func。

我们用 @log 语法将装饰器应用到了 my_func 函数上,相当于执行了如下语句:

my_func = log(my_func)

这样,再用 my_func() 函数时,log函数把my_func() 函数的参数打包给了wrapper()函数,wrapper()函数在执行之前会对my_func() 函数进行一些装饰!然后再执行my_func() 函数。

那么问题来了?为什么会执行wrapper()函数呢,我们仅仅是定义了它,并没有调用,看到log函数结束时的返回语句:return wrapper 就是对wrapper的调用,那这样顺序就里清楚了

当然你也可以选择不用装饰器,实现相同的效果,那样程序就得改成这样:

def log(func,*args,**kw):
    print('call %s():' % func.__name__)
    return func(*args, **kw)
def my_func(*args, **kw):
    print('hello world')
log(my_func)

这样你每次运行my_func函数的时候外面就得套个log函数,而且当my_func函数有参数时,你还得把参数传递给log,就显得调用起来十分麻烦!装饰器的精髓就是装饰器函数把它需要修饰函数的参数一同打包传递给了内部定义的函数,并且执行内部定义的函数

装饰器的其它应用

一个计时装饰器的例子

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数 {func.__name__} 执行时间:{end_time - start_time}")
        return result
    return wrapper

@timer
def my_func(n):
    time.sleep(n)

my_func(2)  # 输出:函数 my_func 执行时间:2.0025088787078857

这个不必多说,和上面log()装饰是一个道理,你可以尝试不用装饰器实现上面的功能!

在这个例子中,定义了一个计时装饰器 timer,它会在函数执行前记录开始时间,执行后记录结束时间,并输出函数执行时间。然后,我们用 @timer 将它应用到了 my_func 函数上,这样每次执行 my_func(n) 函数时,都会输出函数执行时间

Python中一些特定的装饰器

1、@property:用于将一个方法转化为只读属性。可以让我们在不改变原有代码的情况下,对类的外部接口进行改进

class MyClass:
    def __init__(self, x):
        self._x = x

    @property
    def x(self):
        return self._x

c = MyClass(10)
print(c.x)  # 输出:10

在这个例子中,定义了一个类 MyClass,其中包含一个私有属性 _x,和一个方法 x,并用 @property 装饰器将方法转化为只读属性。这样,在外部访问 c.x 时,实际上是调用了 c.x() 方法

2、@staticmethod:用于将一个方法转化为静态方法。静态方法可以直接通过类名调用,不需要实例化对象

class MyClass:
    @staticmethod
    def f(x, y):
        return x + y

print(MyClass.f(10, 20))  # 输出:30

在这个例子中,我们定义了一个类 MyClass,其中包含一个静态方法 f,并用 @staticmethod 装饰器将其转化为静态方法。这样,在外部调用 MyClass.f 时,不需要实例化 MyClass 对象。

3、@classmethod:用于将一个方法转化为类方法。类方法的第一个参数是类对象,可以通过它访问类的属性和方法。

class MyClass:
    x = 10

    @classmethod
    def f(cls):
        return cls.x

print(MyClass.f())  # 输出:10

在这个例子中,我们定义了一个类 MyClass,其中包含一个类属性 x,和一个类方法 f,并用 @classmethod 装饰器将其转化为类方法。这样,在类方法中可以通过第一个参数 cls 访问类属性 x

4、@functools.wraps:用于修饰装饰器。如果我们定义一个装饰器,它会修改被装饰函数的行为,但是在外部调用被装饰函数时,函数的名称和文档字符串会发生改变。为了保留原有的名称和文档字符串,可以使用 @functools.wraps 装饰器。

import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("Calling decorated function")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """Docstring"""
    print("Called example function")

print(example.__name__)        # 输出:'example'
print(example.__doc__)         # 输出:'Docstring'

如果对Python编程感兴趣,可以私聊小编!

#编程# #Python#

展开阅读全文

页面更新:2024-03-08

标签:字符串   静态   语法   函数   属性   例子   定义   对象   参数   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top