>  기사  >  백엔드 개발  >  Python 데코레이터 - 클로저 및 함수 데코레이터

Python 데코레이터 - 클로저 및 함수 데코레이터

PHPz
PHPz앞으로
2023-04-10 14:51:071651검색

1. 클로저

데코레이터를 배우기 전에 클로저의 개념을 이해해야 합니다. 클로저 형성의 핵심 포인트:

  • 함수 중첩
  • 내부 함수를 외부 함수의 반환값으로 사용
  • 내부 함수는 외부 함수의 변수를 사용해야 함

다음은 계산하는 경우 클로저를 설명하기 위한 목록의 평균 패키지:

def make_average():
# 创建一个列表,用来保存数值
nums = []

# 定义一个内部函数,用来计算列表的平均值
def average(n):
# 将数值添加到列表中
nums.append(n)
# 返回平均值
return sum(nums) / len(nums)

return average
  1. 먼저 make_average 함수를 정의합니다.
  2. 두 번째로 값을 저장하기 위해 make_average 함수 내에 빈 목록을 정의합니다.
  3. 세 번째로 평균을 계산하는 내부 함수를 정의합니다.
  4. 마지막으로 이 내부 함수를 외부 함수 make_average의 반환 값으로 사용합니다. ()를 추가하면 이 함수에 대한 호출이 됩니다.
# 调用外部函数,并将其复制给一个变量,注意:此时返回的是内函数的内存地址
a = make_average()
# 给这个变量加(),就相当于调用了内函数average
print(a(20))
print(a(30))

실행 결과는 다음과 같습니다. 전달된 값이 20이면 목록에 숫자가 하나만 있으므로 계산 결과는 20이고, 다른 값 30이 전달되면 20과 30이라는 두 개의 숫자가 있습니다. 이므로 평균을 계산한 결과는 25입니다.

Python 데코레이터 - 클로저 및 함수 데코레이터

2. 데코레이터

1. 데코레이터 소개

예를 들어, 두 숫자의 합과 점수를 계산하는 두 가지 함수가 있습니다. 각각:

def add(a, b):
"""计算两数之和"""
res = a + b
return res

def mul(a, b):
"""计算两数之积"""
res = a * b
return res

이제 요구 사항이 있습니다. 각 함수의 계산이 시작되기 전에 "계산 시작..."을 인쇄하고, 계산이 끝난 후에 "계산 종료..."를 인쇄하고 싶습니다. 함수 코드를 직접 수정하여 이러한 요구를 충족할 수 있지만 다음과 같은 문제에 직면하게 됩니다.

  1. 수정할 함수가 너무 많으면 10개 또는 100개라도 비현실적입니다.
  2. 편리하지 않습니다. 예를 들어, "계산 시작..."을 인쇄하고 싶지 않지만 "시작..."을 인쇄하려면 다시 수정해야 하지 않을까요?
  3. 개방 및 폐쇄 원칙을 위반합니다. (OCP) 즉, 프로그램의 설계에는 개방성이 필요합니다. 프로그램 확장 및 프로그램 수정 종료

그래서 위의 기능 코드를 직접 수정하는 방법은 불가능합니다. 우리는 원래 기능을 수정하지 않고 기능을 확장하기를 희망합니다. 예:

def new_add(a, b):
print("开始计算...")
r = add(a, b)
print("计算结束...")
return r


print(new_add(22, 33))

실행 결과는 다음과 같습니다.

Python 데코레이터 - 클로저 및 함수 데코레이터

이 새 함수 생성 방법은 원래 함수를 수정하지 않지만 매우 심각한 문제에 직면합니다.

지정된 함수만 확장할 수 있고 확장할 수 없습니다. 예를 들어, 위의 add 함수는 확장할 수 없지만 mul 함수를 확장하려면 확장 함수만 생성하면 됩니다. 모든 기능의 범위를 지정할 수 있는 범용 확장 기능입니다. 원래 함수 코드를 변경하지 않는 이러한 유형의 범용 함수는 데코레이터입니다.

2. 함수 데코레이터

데코레이터는 본질적으로 다른 함수나 클래스가 코드를 수정하지 않고도 추가 함수를 추가할 수 있도록 하는 Python 함수 또는 클래스입니다. 즉, 기존 개체에 함수를 추가하는 것입니다. 데코레이터는 함수/클래스 객체이기도 합니다. 로그 삽입, 성능 테스트, 트랜잭션 처리, 캐싱, 권한 확인 등과 같은 교차 요구 사항이 있는 시나리오에서 자주 사용됩니다.

1) 장식된 함수는 매개변수를 사용하지 않습니다

예:

def wrapper_info(func):
def inner():
print("开始介绍...")
res = func()
print("介绍结束...")
return res

return inner

def introduce1():
print("我是周润发,我来自HONG KONG")

info = wrapper_info(introduce1)
info()

실행 결과는 다음과 같습니다.

Python 데코레이터 - 클로저 및 함수 데코레이터원본 함수 코드를 변경하지 않고 일부 추가 함수가 원래 함수, func 데코레이션할 함수입니다. 데코레이션 함수에 변수로 전달되며 다른 함수에도 보편적으로 사용할 수 있습니다. 하지만 현재 문제는 장식된 함수가 매개변수를 취하는 경우 어떻게 될까요? 예:

def introduce2(name, age):
print(f"我叫{name}, 我今年{age}岁了")

2) 데코레이팅된 함수에는 매개변수가 있습니다

이름과 나이는 데코레이터 Wrapper_info에서 전달될 수 있지만 모든 데코레이팅된 함수에는 이름, 나이 또는 지정된 유형의 매개변수만 전달될 수도 있습니다. 사전, 목록, 튜플 등에서 즉, 전달되는 매개변수의 종류와 개수가 고정되어 있지 않으면 어떻게 해야 할까요?

이때 가변 길이 매개변수를 사용해야 합니다: (*args, **kwargs)

def wrapper_info(func):
"""
用来对其他函数进行扩展,使其他函数可以在执行前做一些额外的动作
:param func: 要扩展的函数对象
:return:
"""
def inner(*args, **kwargs):
print("开始介绍...")
res = func(*args, **kwargs)
print("介绍结束...")
return res

return inner

예:

def introduce3(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")

실행 결과는 다음과 같습니다.

3)装饰器带参数

上述提到的是装饰器,一种是应用于被装饰的函数不带参数,一种是被装饰的函数带参数,那装饰器本身能否带参数呢?比如我定义一个变量,想通过传入不同的值来控制这个装饰器实现不同的功能。答案是肯定的,例如:

def use_log(level):
def decorator(func):
def inner(*args, **kwargs):
if level == "warn":
logging.warning("%s is running by warning" % func.__name__)
elif level == "info":
logging.warning("%s is running by info" % func.__name__)
else:
logging.warning("%s is running by other" % func.__name__)
return func(*args, **kwargs)

return inner

return decorator


def introduce4(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")


info1 = use_log(introduce4('周星驰', 28, '香港'))
info1('info')
info2 = use_log(introduce4('周润发', 28, '香港'))
info2('warn')
info3 = use_log(introduce4('成龙', 28, '香港'))
info3('xxx')

运行结果如下:

Python 데코레이터 - 클로저 및 함수 데코레이터

3.装饰器调用

方式一:以函数方式调用

info3 = wrapper_info(introduce3)
info3('刘德华', 28, '香港')

如果是装饰器函数带参数,则调用方式为:

info4 = use_log(introduce4('周星驰', 28, '香港'))
info4('info')

方式二:以语法糖方式调用

即在被装饰函数上方以@符号进行修饰

@wrapper_info
def introduce3(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")

introduce3('刘德华', 28, '香港')

如果是装饰器函数带参数,例如上述的use_log,则需要在装饰器中传入参数:

@use_log('info')
def introduce4(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")

小结

什么是装饰器?

在不改变原函数代码的情况下,给原函数增加了一些额外的功能,并且能够通用于其他函数,这样的函数就称作为装饰器。

装饰器的调用

可以通过传统调用函数的方式进行调用,也可以通过@装饰器的方式调用

装饰器的特点

  • 通过装饰器,可以在不修改原来函数的情况下对函数进行扩展
  • 一个函数可以同时指定多个装饰器

위 내용은 Python 데코레이터 - 클로저 및 함수 데코레이터의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 51cto.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제