开放的编程资料库

当前位置:我爱分享网 > Python教程 > 正文

Python装饰器

Python装饰器教程展示了如何在Python中使用装饰器函数。

Python函数是一等公民。这意味着函数与Python中的其他对象具有同等地位。函数可以分配给变量、存储在集合中、动态创建和删除,或者作为参数传递。

嵌套函数,也称为内部函数,是在另一个函数中定义的函数。

Python装饰器

Python装饰器在不修改可调用对象本身的情况下扩展和修改可调用对象的行为。装饰器是装饰(或包装)其他函数并在包装函数运行前后执行代码的函数。

Python装饰器通常用于日志记录、身份验证和授权、计时和缓存。

Python装饰器示例

在下一个示例中,我们创建一个简单的装饰器示例。

#!/usr/bin/python

def enclose(fun):

    def wrapper():

        print("***************************")
        fun()
        print("***************************")

    return wrapper

def myfun():
    print("myfun")

enc = enclose(myfun)
enc()

enclose函数是一个装饰器,它通过在其输出中添加星号来扩展装饰函数。

def enclose(fun):
...

enclose函数接受一个函数作为参数。

def wrapper():

    print("***************************")
    fun()
    print("***************************")

return wrapper

wrapper用星星装饰传递的函数。返回包装函数。

def myfun():
    print("myfun")

这是一个需要修饰的常规函数​​。

enc = enclose(myfun)
enc()

myfun被传递给enclose函数,它在其中被扩展。返回并调用包装函数。

$ ./enclose.py
***************************
myfun
***************************

这是输出。装饰器在常规函数的输出前后添加星星。

带有@符号的Python装饰器

Python允许使用@符号来标记要用装饰器装饰的方法。

#!/usr/bin/python

def enclose(fun):

    def wrapper():

        print("***************************")
        fun()
        print("***************************")

    return wrapper

@enclose
def myfun():
    print("myfun")

myfun()

在功能上,该示例与上一个示例等效。只是使用了不同的语法。

用参数修饰函数

以下示例展示了如何修饰带参数的函数。

#!/usr/bin/python

def enclose(fun):

    def wrapper(val):

        print("***************************")
        fun(val)
        print("***************************")

    return wrapper

@enclose
def myfun(val):
    print(f"myfun with {val}")

myfun('falcon')

在此代码示例中,常规函数采用一个参数。

#!/usr/bin/python

def enclose(fun):

    def wrapper(*args, **kwargs):

        print("***************************")
        fun(*args, **kwargs)
        print("***************************")

    return wrapper

@enclose
def myfun(name, age):
    print(f'{name} is {age} years old')

myfun(name='Peter', age=32)
myfun('Roman', 29)

此示例展示了如何使用*args,**kwargs语法处理可变数量的参数。

Python装饰器修改数据

装饰函数可以修改被装饰函数的数据。

#!/usr/bin/python

def uppercase(fun):

    def wrapper():

        res = fun()
        modified = res.upper()

        return modified
    return wrapper


@uppercase
def gen_message():
    return 'Hello there!'

msg = gen_message()
print(msg)

@uppercase装饰器将返回的文本更改为大写。

def uppercase(fun):

    def wrapper():

        res = fun()
        modified = res.upper()

        return modified
    return wrapper

在包装函数中,文本被修改并返回。

$ ./uppercase.py
HELLO THERE!

Python多重堆叠装饰器

可以在一个函数上应用多个装饰器。

#!/usr/bin/python

def strong(fun):

    def wrapper():
        return f'<strong>{fun()}</strong>'
    return wrapper

def em(fun):

    def wrapper():
        return f'<em>{fun()}</em>'

    return wrapper


@strong
@em
def message():
    return 'This is some message'


print(message())

在示例中,我们在文本上应用了两个HTML标记。

$ ./multiple_decors.py
<strong><em>This is some message</em></strong>

Python装饰器计时示例

在下面的例子中,我们在一个函数上应用了一个定时器装饰器。

#!/usr/bin/python

import time
import math

def timer(func):

    def wrapper(*args, **kwargs):

        begin = time.time()

        f = func(*args, **kwargs)

        end = time.time()
        print("Total time taken in : ", func.__name__, end - begin)

        return f

    return wrapper


@timer
def factorial(num):

    return math.factorial(num)

f = factorial(4580)
print(f)

该示例使用装饰器计算factorial函数运行的时间。

begin = time.time()

在函数运行之前,我们获取开始时间。

end = time.time()
print("Total time taken in : ", func.__name__, end - begin)

函数运行后,我们得到结束时间并打印差值。

functools@wraps装饰器

应用装饰器函数后,原函数的__name____doc____module__属性都丢失了。这使得调试变得尴尬。要解决此问题,我们可以使用functool的@wraps装饰器。

#!/usr/bin/python

from functools import wraps

def enclose(fun):

    @wraps(fun)
    def wrapper():
        '''This is wrapper function'''

        print("***************************")
        fun()
        print("***************************")

    return wrapper

@enclose
def myfun():
    '''this is myfun()''' 
    print("myfun")

myfun()

print(myfun.__name__)
print(myfun.__doc__)

在示例中,我们在包装函数上应用了@wraps装饰器。保留原始函数(myfun)的名称和文档字符串。

$ ./naming.py
***************************
myfun
***************************
myfun
this is myfun()

Python类装饰器

可以使用类作为装饰器。为此,我们需要实现__call__魔法函数。

#!/usr/bin/python

import functools

class CountCalls:

    def __init__(self, fun):

        functools.update_wrapper(self, fun)
        self.fun = fun
        self.num_of_calls = 0

    def __call__(self, *args, **kwargs):

        self.num_of_calls += 1
        print(f"Call {self.num_of_calls} of {self.fun.__name__} fun")
        return self.fun(*args, **kwargs)

@CountCalls
def hello():
    print("Hello there!")

hello()
hello()
hello()

在示例中,我们使用类装饰器来计算常规函数的调用。

def __init__(self, fun):

    functools.update_wrapper(self, fun)
    self.fun = fun
    self.num_of_calls = 0

我们调用update_wrapper函数。它与@wraps装饰器具有相同的目的;即它保留原始函数的元数据(__name____doc__)。我们保留对原始函数的引用并设置num_of_calls变量。

def __call__(self, *args, **kwargs):

    self.num_of_calls += 1
    print(f"Call {self.num_of_calls} of {self.fun.__name__} fun")
    return self.fun(*args, **kwargs)

我们增加num_of_calls变量,打印一条消息,并调用原始函数,向其传递可能的参数。

$ ./counting_calls.py
Call 1 of hello fun
Hello there!
Call 2 of hello fun
Hello there!
Call 3 of hello fun
Hello there!

Python@staticmethod装饰器

Python有@staticmethod内置装饰器,它在Python类中创建一个静态方法。静态方法属于类,无需创建实例即可调用。

#!/usr/bin/python

class Math:

    @staticmethod
    def abs(x):
        
        if x < 0:
            return -x
        return x


print(Math.abs(3))
print(Math.abs(-3))

在示例中,我们使用@staticmethod装饰器创建静态abs方法。通过指定类名并使用点运算符调用该方法:Math.abs

烧瓶装饰器

流行的Python框架Flask使用装饰器。例如,@app.route用于定义路由。

#!/usr/bin/python

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello there!'

在示例中,hello函数使用Flask的@app.route装饰器映射到根页面。

在本教程中,我们使用了Python装饰器。

列出所有Python教程。

未经允许不得转载:我爱分享网 » Python装饰器

感觉很棒!可以赞赏支持我哟~

赞(0) 打赏