>  기사  >  백엔드 개발  >  Python 데코레이터 함수 자세히 살펴보기

Python 데코레이터 함수 자세히 살펴보기

WBOY
WBOY앞으로
2022-06-24 12:50:421929검색

이 글은 python에 대한 관련 지식을 제공합니다. 주로 데코레이터의 형성 과정, 본질과 기능, 데코레이터의 발전과 최적화 등을 포함한 데코레이터 기능과 관련된 문제를 정리합니다. 다음은 살펴보겠습니다. 모두에게 도움이 되기를 바랍니다. .

Python 데코레이터 함수 자세히 살펴보기

추천 학습: python

f

def f():
    print('hello')

함수를 작성하고 이 함수를 실행하는 데 걸리는 시간을 알고 싶다면 쉽습니다. 코드를 다음으로 변경하면 됩니다. 다음

import time
def f():
    start = time.time()   #获取程序执行开始的时间
    print('hello')
    end = time.time()     #获取程序执行结束的时间
    print(end - start)    #得出函数f执行所要时间

f()

그런데 f2, f3...fn이라는 수많은 함수를 작성했는데, 각 함수를 실행하는 데 걸리는 시간을 알고 싶었습니다. 위와 같이 모두 변경하면 매우 귀찮지 않을까요? 여전히 불가능합니다. 너무 많은 문제가 발생하기 때문입니다. 무엇을 해야 할까요? 그래서 아이디어가 나서 타이머 함수를 작성했습니다. . .

import time
def timer(func):
    start = time.time()
    func()
    print(time.time() - start)

def f():
    print('hello')


def f2():
    print('xorld')

timer(f)
timer(f2)

훨씬 더 단순해 보이지 않나요? 함수를 아무리 많이 작성하더라도 이 타이밍 함수를 호출하여 함수의 실행 시간을 계산할 수 있습니다

하지만 이 함수를 원래 방식으로 호출하고 싶다면 f1(), f2(), fn(), 함수는 원래대로 실행될 것입니다. 출력 결과가 변하지 않는다는 전제 하에, 타이머(f)를 호출하는 대신에 타이머(f2)가 시간을 계산할 수 있는 기능을 추가할 수 있습니다.

아래 데코레이터 함수를 읽으면 이 문제를 해결하는 방법을 알 수 있습니다



1. 데코레이터 - 형성 과정

다음은 위 문제를 해결하기 위한 간단한 버전의 코드입니다.

import time

def f():
    print('hello')

def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

f = timer(f)
f()

이것은 제가 이 함수를 원래 방식으로 f1(), f2(), fn()으로 호출하고 싶다고 말했습니다. 이 함수는 원래 실행 출력 결과를 변경하지 않고 계산 시간을 늘릴 수도 있지만 여전히 함수 f에서 실행해야 합니다. 이 코드 문자열에 f = 타이머(f)를 쓰는 것이 귀찮습니까? Python 개발자도 이것이 귀찮다고 생각하므로 Python 개발자는 이 문제를 해결하기 위해 구문 설탕을 제공합니다!



2. 데코레이터 - 구문 설탕에 대한 첫 소개

f = 타이머(f)를 @timmer로 바꾸는 구문입니다.

import time
def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

@timer   #==> 写着这句话就相当于执行了f = timer(f)
def f():
    print('hello')


f()


3. 데코레이터 - 본질과 함수

1. 본질

데코레이터의 본질은 클로저 함수

2. 함수

원래 함수와 호출 방식을 수정하지 않고 함수 확장

4. 데코레이터 - 매개변수와 반환값이 있는 데코레이터 ​​

  • 1. 하나의 매개변수로 함수 데코레이터

방금 작성한 데코레이터는 모두 매개변수 없이 함수를 데코레이팅하는 것입니다. 매개변수로 함수를 꾸미고 싶다면 이제 어떻게 해야 하나요?

import time
def timer(func):
    def inner(a):
        start = time.time()
        func(a)
        print(time.time() - start)
    return inner

@timer
def f(a):
    print(a)

f('hello')
  • 2. 매개변수는 다르지만 반환값은 없는 여러 함수 꾸미기

사실 함수를 매개변수로 꾸미는 건 어렵지 않지만, 함수가 두 개 있으면 전달해야 하는 매개변수가 다릅니다. 예를 들어, 함수 func1에는 두 개의 매개변수 func1(a,b)가 있고, 함수 func2에는 하나의 매개변수 func2(a)만 있으며, 둘 다 함수 실행 시간을 계산하기 위해 이 데코레이터로 장식되기를 원합니까? 무엇을 해야 할까요? 그런 다음 다음 코드를 사용하십시오.

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func1 = timer(func1)
def func1(a,b):
    print('in func1')

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))

输出结果:
in func1
0.0
in func2 and get a:aaaaaa
0.0
fun2 over
  • 3. 다양한 매개변수와 반환값으로 여러 함수 꾸미기

이제 매개변수 문제는 완벽하게 해결됐지만, 함수에 반환값이 있다면 어떨까요? 위의 코드로는 반환 값을 얻을 수 없습니다. 그렇다면 이 문제를 해결하는 방법은 무엇입니까? 그럼 아래 코드를 보세요!

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func2('aaaaaa')
print(func2('aaaaaa'))

输出结果:
in func2 and get a:aaaaaa
0.0
in func2 and get a:aaaaaa
0.0
fun2 over
  • 4. 여러 데코레이터가 동일한 기능을 장식합니다

때때로 동일한 기능을 장식하기 위해 여러 데코레이터를 사용할 수도 있습니다.

ef wrapper1(func):   #func ----- f
    def inner1():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner1

def wrapper2(func):
    def inner2():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner2

@wrapper2       #f = wrapper2(f) ----->> wrapper2(inner1)  == inner2
@wrapper1       #f = wrapper1(f) = inner
def f():
    print('in f')
f()    #===>>inner2
#多个装饰器装饰同一个函数

输出结果:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func


5. 데코레이터 개선 및 최적화

  • 1. 매개변수가 있는 데코레이터

위의 데코레이터는 이미 매우 아름답지만 코드에 수많은 함수를 주면 여전히 문제가 있습니다. @timer의 구문 설탕이 추가되었습니다. 더 이상 사용하고 싶지 않으면 하나씩 다시 주석 처리하고 3일 동안 밤낮으로 작업해야 하지 않나요? 특별히 번거롭지 않나요? 사용하지 않을 때 데코레이터를 하나씩 주석을 달거나 삭제하는 대신 더 잘 재활용하기 위해 매개변수가 있는 데코레이터의 개념을 소개합니다

'''
为了使装饰器不用时能够更好的回收而不是一个一个去注释或者删除
我们引入带参数的装饰器概念
'''

import time
'''FLAGE的目的是用它控制装饰器的开关,
那么当我们不用的时候就不要一个一个去注释只需将True改为False就行'''

FLAGE = True
def timmer_out(flag):
    def timmer(func):
        def inner(*args,**kwargs):
            if flag:
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end - start)
                return ret
            else:
                ret = func(*args, **kwargs)
                return ret
        return inner
    return timmer
@timmer_out(FLAGE)

#timmer_out(FLAGE)
# 也相当于执行  timmer_out(FLAGE)--->>返回timmer———————>>@timmer(wahaha = timmer(wahaha))
def wahaha():
    time.sleep(0.1)         #不休息的话函数执行的太快难以计算时间
    print('wahahahahahaha')

wahaha()

@timmer_out(FLAGE)
def erguotou():
    time.sleep(0.1)         #不休息的话函数执行的太快难以计算时间
    print('erguotoutoutou')

erguotou()

输出结果:
wahahahahahaha
0.10152268409729004
erguotoutoutou
0.10795140266418457
  • 2、防止函数必要信息失效

'''
print(wahaha.__name__)      #查看字符串格式的函数名
print(wahaha.__doc__)       #查看一个函数的注释
'''
#下面用__name__查看holiday的函数名

from functools import wraps
def wrapper(func):
    @wraps(func)            #加在最内层函数正上方
    def inner(*args,**kwargs):
        print('在被装饰的函数执行之前做的事')
        ret = func(*args,**kwargs)
        print('在被装饰的函数执行之后做的事')
        return ret
    return inner

@wrapper        #holiday = wrapper(holiday)
def holiday(day):
    '''
    这是一个放假通知
    :param day:
    :return:
    '''
    print('全体放假%s天'%day)
    return '好开心'

print(holiday.__name__)
print(holiday.__doc__)
'''
结果是inner和None 但我们想要的是打印holiday的字符串格式的函数名和函数的注释这时该怎么办?
解决方法就是  from functools import wraps
使用语法是@wraps(被装饰的函数名)
'''

输出结果:
holiday

    这是一个放假通知
    :param day:
    :return:


六、装饰器 —— 装饰原则

  • 1、开放封闭原则

1.对原函数的功能扩展是开放的

        为什么要对功能扩展开放呢?

    对于任何一个程序来说,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许后来扩展、添加新功能。

2.对修改是封闭的

        为什么要对修改封闭呢?

                就像我们刚刚提到的,因为我们写的一个函数,很有可能在其他地方已经被导入使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经正在使用该函数的代码。

装饰器就完美遵循了这个开放封闭原则。这就是学装饰器的初衷



小结:

  • 1、装饰器的固定格式(模板)

#格式一

def timer(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner

#格式二

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

推荐学习:python

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

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