ホームページ >バックエンド開発 >Python チュートリアル >Python デコレータ関数を詳しく見る

Python デコレータ関数を詳しく見る

WBOY
WBOY転載
2022-06-24 12:50:422034ブラウズ

この記事では、デコレータの形成過程、本質と機能、高度化と最適化など、デコレータ関数に関する論点を中心に整理した 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 (), この関数には、元の実行出力結果を変更せずに時間を計算する関数を追加することもできます。timer(f) を呼び出す代わりに、timer(f2) で時間を計算することもできます。

以下のデコレータ関数を読むと、この問題の解決方法がわかります。



1. デコレータ - 形成プロセス

次は解決策 A です。上記の質問のコードの簡単なバージョン:

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 = timer(f) と書かなければなりません。 Python 開発者もこの問題を煩わしいと感じているため、この問題を解決するための 構文シュガー を提供してくれました。



2. デコレータ - 糖衣構文の最初の紹介

f = timer(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. 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. 戻り値のない、異なるパラメータを持つ複数の関数を修飾する

実際、関数をパラメータで修飾することは難しくありません。しかし、2 つの関数があり、渡す必要があるパラメーターが異なる場合はどうなるでしょうか。たとえば、関数 func1 には 2 つのパラメーター func1(a,b) があり、関数 func2 には 1 つのパラメーター func2(a) しかなく、それらはすべてこのデコレータ Decorator を使用して関数の実行時間を計算しますか?これについてはどうすればよいでしょうか?次に、次のコードを使用します。

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日間?特に面倒なことではありませんか? デコレータを 1 つずつ注釈を付けたり削除したりするのではなく、使用しないときにリサイクルしやすくするために、パラメータを備えたデコレータの概念を導入します

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

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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。