欢迎访问宙启技术站
智能推送

Python函数装饰器使用详解

发布时间:2023-05-31 04:26:08

Python中装饰器(decorator)是一个很重要的概念,使用装饰器可以方便地添加或者更改函数的功能。Python中的函数装饰器主要用于在不修改原函数的情况下增加原函数的功能。本文将对Python的函数装饰器进行详细的说明,包括其语法、使用方法和注意事项。

一、使用方法

1.函数可以作为参数传入

在Python中,函数和其他任何对象一样,可以作为参数传递给其他函数。例如,下面的代码将函数b作为参数传递给了函数a:

def a(b):
    b()
    
def c():
    print('Hello World')
    
a(c)

输出结果为:

Hello World

2.函数可以作为返回值

除了可以作为参数传递给其他函数外,函数还可以作为返回值。例如,下面的代码中,函数a返回了函数c:

def a():
    def c():
        print('Hello World')
    return c

b = a()
b()

输出结果为:

Hello World

3.@符号可以用来装饰函数

在Python中,可以使用@符号来装饰函数。装饰器可以增加或者更改函数的功能,例如,@staticmethod装饰器可以把方法变成静态方法,@classmethod装饰器可以把方法变成类方法。下面是一个简单的例子,使用装饰器来记录函数的运行时间:

import time

def calculate_time(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        print('running time: {}'.format(end - start))
    return wrapper

@calculate_time
def calculate_square(n):
    squares = []
    for i in range(n):
        squares.append(i * i)
    return squares

calculate_square(100000)

输出结果为:

running time: 0.011143922805786133

二、语法

Python中函数装饰器的语法比较简单。一个函数装饰器其实就是一个函数,这个函数接受一个函数作为参数,返回一个函数作为结果。下面是一个简单的例子:

def my_decorator(func):
    def wrapper():
        print('Before the function is called.')
        func()
        print('After the function is called.')
    return wrapper

def say_hello():
    print('Hello World!')

say_hello = my_decorator(say_hello)
say_hello()

输出结果为:

Before the function is called.
Hello World!
After the function is called.

这段代码中,my_decorator是一个函数装饰器,它接受一个函数作为参数,然后返回一个inner函数。inner函数中包含了装饰器的功能。say_hello函数在使用my_decorator装饰后,再次调用时,将首先打印出 "Before the function is called",然后执行say_hello函数,最后打印出 "After the function is called"。

Python提供了一种更加简单的语法,使用 "@" 符号来装饰函数。例如,下面的代码使用 "@" 符号来装饰 say_hello 函数:

def my_decorator(func):
    def wrapper():
        print('Before the function is called.')
        func()
        print('After the function is called.')
    return wrapper

@my_decorator
def say_hello():
    print('Hello World!')

say_hello()

输出结果为:

Before the function is called.
Hello World!
After the function is called.

这段代码中,my_decorator 函数是一个装饰器,@my_decorator 是第二种语法,它使用 "@" 符号来装饰 say_hello 函数。这种装饰器语法与 种的 区别是这个语法更加简洁。

三、应用

1.添加日志记录功能

在日志中记录程序的运行时间、函数的输入和输出值以及函数的调用次数等信息可以帮助我们更好地理解程序的运行过程并调试程序。下面是一个使用装饰器实现日志记录的例子:

import logging

logging.basicConfig(level=logging.INFO)

def log(func):
    def wrapper(*args, **kwargs):
        logging.info('Called function: {}. Args: {}, kwargs: {}'.format(func.__name__, args, kwargs))
        result = func(*args, **kwargs)
        logging.info('Function returned: {}'.format(result))
        return result
    return wrapper

@log
def add(a, b):
    return a + b

add(1, 2)
add('hello', 'world')

输出结果为:

INFO:root:Called function: add. Args: (1, 2), kwargs: {}
INFO:root:Function returned: 3
INFO:root:Called function: add. Args: ('hello', 'world'), kwargs: {}
INFO:root:Function returned: helloworld

这个例子中,使用日志记录来记录每次调用函数 add 的参数和返回值。

2.缓存函数结果

对于一些计算量比较大的函数,我们可以使用缓存技术来避免重复计算,加速程序的运行速度。下面是一个使用装饰器缓存计算结果的例子:

def cache_result(func):
    cache = {}

    def wrapper(*args):
        if args in cache:
            return cache[args]
        else:
            result = func(*args)
            cache[args] = result
            return result

    return wrapper

@cache_result
def fibonacci(n):
    if n < 2:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(100))

使用装饰器将函数 fibonacci 缓存起来,这样下一次调用时就能从缓存中读取结果而避免了重复计算。

3. 身份认证

在应用程序开发中,身份认证往往是必须的功能。可以使用装饰器来实现身份认证的功能:

def login_required(func):
    def wrapper(*args, **kwargs):
        if not is_logged_in():
            login()
        else:
            return func(*args, **kwargs)
    return wrapper

@login_required
def view_profile(username):
    print("This is the profile page of %s." % username)

view_profile('john')

这个例子中,login_required 装饰器检查用户是否登录。如果用户没有登录,则调用 login() 方法,否则调用 view_profile() 方法。

四、注意事项

1.修改函数属性

使用装饰器有一个问题是如果不小心修改了被装饰的函数的属性(如__name__、__doc__等),就会导致一些问题,例如不正确的函数签名,文档不正确等问题。

def my_decorator(func):
    def wrapper():
        """Wrapper function"""
        print('Before the function is called.')
        func()
        print('After the function is called.')
    # 修改属性
    wrapper.__name__ = func.__name__
    wrapper.__doc__ = func.__doc__
    return wrapper

@my_decorator
def say_hello():
    """Say hello"""
    print('Hello World!')

print(say_hello.__name__)
print(say_hello.__doc__)

输出结果为:

say_hello
Say hello

这个例子中,修改 wrapper 函数的属性,以使它的属性与被装饰的函数相同。

2.装饰带参数的函数

如果要装饰带参数的函数,可以在装饰器中使用可变参数 *args 和可变关键字参数 **kwargs。例如,下面的代码给带参数的函数 add 进行了装饰:

`python

def log(func):

def wrapper(*args, **kwargs):

logging.info('Called function: {}. Args: {}, kwargs: {}'.format(func.__name__, args, kwargs))

result = func(*args, **kwargs)

logging.info('Function returned: {}'.format(result))

return result

return wrapper

@log

def add(a, b):

return a + b

add(1, 2)

add(3,