Heim Backend-Entwicklung Python-Tutorial Um Python-Dekoratoren zu verstehen, reicht es aus, diesen Artikel zu lesen

Um Python-Dekoratoren zu verstehen, reicht es aus, diesen Artikel zu lesen

Feb 17, 2017 am 11:07 AM

Bevor ich über Python-Dekoratoren spreche, möchte ich zunächst ein Beispiel geben, das zwar etwas schmutzig ist, aber für das Thema Dekoratoren sehr relevant ist.

Die Hauptfunktion der Unterwäsche, die jeder hat, besteht darin, unsere Schande zu verdecken, aber im Winter kann sie uns nicht vor Wind und Kälte schützen, was sollen wir tun? Eine Möglichkeit bestand darin, die Unterwäsche dicker und länger zu machen. Auf diese Weise hat sie nicht nur die Funktion, Scham zu bedecken, sondern sorgt auch für Wärme. Nachdem wir die Unterwäsche in eine Hose verwandelt haben , , obwohl es immer noch eine schamverdeckende Funktion hat, ist es im Grunde keine echte Unterwäsche mehr. Also haben kluge Leute Hosen erfunden und die Hosen direkt über die Unterwäsche gezogen, ohne die Unterwäsche zu beeinträchtigen. Mit der Hose wird dem Baby nicht mehr kalt. Dekorateure sind wie die Hosen, von denen wir hier sprechen. Sie spenden unserem Körper Wärme, ohne die Funktion der Unterwäsche zu beeinträchtigen.

Bevor Sie über Dekoratoren sprechen, müssen Sie zunächst eines verstehen: Funktionen in Python unterscheiden sich von Java und C++. Funktionen in Python können als Parameter wie gewöhnliche Variablen übergeben werden, zum Beispiel:

def foo():
    print("foo")def bar(func):
    func()bar(foo)
Nach dem Login kopieren

Offiziell zurück zu unserem Thema. Ein Dekorator ist im Wesentlichen eine Python-Funktion oder -Klasse, die es anderen Funktionen oder Klassen ermöglicht, zusätzliche Funktionalität hinzuzufügen, ohne Codeänderungen vorzunehmen. Der Rückgabewert des Dekorators ist ebenfalls ein Funktions-/Klassenobjekt. Es wird häufig in Szenarien mit übergreifenden Anforderungen verwendet, z. B. Protokolleinfügung, Leistungstests, Transaktionsverarbeitung, Caching, Berechtigungsüberprüfung usw. Dekoratoren eignen sich hervorragend zur Lösung solcher Probleme. Mit Dekoratoren können wir eine große Menge ähnlichen Codes, der nichts mit der Funktion selbst zu tun hat, in Dekoratoren extrahieren und ihn weiterhin wiederverwenden. Kurz gesagt besteht der Zweck eines Dekorateurs darin, einem vorhandenen Objekt zusätzliche Funktionalität hinzuzufügen.

Schauen wir uns zunächst ein einfaches Beispiel an, obwohl der eigentliche Code möglicherweise viel komplizierter ist:

def foo():
    print('i am foo')
Nach dem Login kopieren

Jetzt gibt es eine neue Anforderung, in der wir hoffen, das Ausführungsprotokoll des aufzuzeichnen Funktion, also fügen Sie im Code den Protokollcode hinzu:

def foo():
    print('i am foo')
    logging.info("foo is running")
Nach dem Login kopieren

Wenn die Funktionen bar() und bar2() ähnliche Anforderungen haben, was sollte getan werden? Eine andere Protokollierung in der Bar-Funktion schreiben? Dies führt zu viel ähnlichem Code. Um das wiederholte Schreiben von Code zu reduzieren, können wir dies tun und eine neue Funktion neu definieren: Nachdem das Protokoll verarbeitet wurde, wird der eigentliche Geschäftscode ausgeführt

def use_logging(func):
    logging.warn("%s is running" % func.__name__)
    func()def foo():
    print('i am foo')use_logging(foo)
Nach dem Login kopieren

So gibt es logischerweise kein Problem, die Funktion ist implementiert, aber wenn wir sie aufrufen, rufen wir nicht mehr die eigentliche Geschäftslogik-Foo-Funktion auf, sondern ersetzen sie durch die Funktion use_logging, die jetzt die ursprüngliche Codestruktur zerstört Gibt es einen besseren Weg, anstatt jedes Mal die ursprüngliche Funktion foo als Parameter an die Funktion use_logging zu übergeben? Natürlich gibt es das, die Antwort lautet: Dekorateure.

Einfacher Dekorator

def use_logging(func):

    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()   # 把 foo 当做参数传递进来时,执行func()就相当于执行foo()
    return wrapperdef foo():
    print('i am foo')foo = use_logging(foo)  # 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper,这条语句相当于  foo = wrapperfoo()                   # 执行foo()就相当于执行 wrapper()
Nach dem Login kopieren

use_logging ist ein Dekorator, eine gewöhnliche Funktion, die die Funktion func umschließt, die die eigentliche Geschäftslogik ausführt. Es sieht so aus, als wäre foo mit use_logging dekoriert Wie oben gibt use_logging auch eine Funktion zurück, und der Name dieser Funktion ist Wrapper. In diesem Beispiel wird das Ein- und Aussteigen einer Funktion als Querschnitt bezeichnet. Diese Programmiermethode wird aspektorientierte Programmierung genannt.

@-Syntaxzucker

Wenn Sie schon länger mit Python in Kontakt sind, müssen Sie mit dem @-Symbol vertraut sein. Ja, das @-Symbol ist ein syntaktischer Zucker für Dekoratoren in Funktionen Der Ort, an dem die Definition beginnt, sodass der letzte Schritt der Neuzuweisung weggelassen werden kann.

def use_logging(func):

    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()
    return wrapper@use_loggingdef foo():
    print("i am foo")foo()
Nach dem Login kopieren

Wie oben gezeigt, können wir mit @ den Satz foo = use_logging(foo) weglassen und direkt foo() aufrufen, um das gewünschte Ergebnis zu erhalten. Haben Sie gesehen, dass die Funktion foo() in keiner Weise geändert werden muss? Fügen Sie einfach dort, wo sie definiert ist, einen Dekorator hinzu. Der Aufruf ist immer noch derselbe wie zuvor. Wenn wir andere ähnliche Funktionen haben, können wir mit dem Aufruf fortfahren Der Dekorateur kann die Funktion dekorieren, ohne die Funktion wiederholt ändern oder neue Pakete hinzufügen zu müssen. Dadurch verbessern wir die Wiederverwendbarkeit des Programms und erhöhen die Lesbarkeit des Programms.

Der Grund, warum Dekoratoren in Python so bequem zu verwenden sind, liegt darin, dass Python-Funktionen als Parameter an andere Funktionen wie gewöhnliche Objekte übergeben werden können, anderen Variablen zugewiesen werden können und als Rückgabewerte verwendet werden können innerhalb einer anderen Funktion definiert.

*args, **kwargs

Jemand fragt sich vielleicht, was passiert, wenn meine Geschäftslogikfunktion foo Parameter erfordert? Zum Beispiel:

def foo(name):
    print("i am %s" % name)
Nach dem Login kopieren

Wir können die Parameter angeben, wenn wir die Wrapper-Funktion definieren:

def wrapper(name):
        logging.warn("%s is running" % func.__name__)
        return func(name)
    return wrapper
Nach dem Login kopieren

这样 foo 函数定义的参数就可以定义在 wrapper 函数中。这时,又有人要问了,如果 foo 函数接收两个参数呢?三个参数呢?更有甚者,我可能传很多个。当装饰器不知道 foo 到底有多少个参数时,我们可以用 *args 来代替:

def wrapper(*args):
        logging.warn("%s is running" % func.__name__)
        return func(*args)
    return wrapper
Nach dem Login kopieren

如此一来,甭管 foo 定义了多少个参数,我都可以完整地传递到 func 中去。这样就不影响 foo 的业务逻辑了。这时还有读者会问,如果 foo 函数还定义了一些关键字参数呢?比如:

def foo(name, age=None, height=None):
    print("I am %s, age %s, height %s" % (name, age, height))
Nach dem Login kopieren

这时,你就可以把 wrapper 函数指定关键字函数:

def wrapper(*args, **kwargs):
        # args是一个数组,kwargs一个字典
        logging.warn("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper
Nach dem Login kopieren

带参数的装饰器

装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,该装饰器接收唯一的参数就是执行业务的函数 foo 。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的。

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            elif level == "info":
                logging.info("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator@use_logging(level="warn")def foo(name='foo'):
    print("i am %s" % name)foo()
Nach dem Login kopieren

上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level="warn")调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。

@use_logging(level="warn")等价于@decorator

类装饰器

没错,装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')@Foodef bar():
    print ('bar')bar()
Nach dem Login kopieren

functools.wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看例子:

# 装饰器def logged(func):
    def with_logging(*args, **kwargs):
        print func.__name__      # 输出 'with_logging'
        print func.__doc__       # 输出 None
        return func(*args, **kwargs)
    return with_logging# 函数@loggeddef f(x):
   """does some math"""
   return x + x * xlogged(f)
Nach dem Login kopieren

不难发现,函数 f 被with_logging取代了,当然它的docstring,__name__就是变成了with_logging函数的信息了。好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数 foo 一样的元信息了。

from functools import wrapsdef logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__      # 输出 'f'
        print func.__doc__       # 输出 'does some math'
        return func(*args, **kwargs)
    return with_logging@loggeddef f(x):
   """does some math"""
   return x + x * x
Nach dem Login kopieren

装饰器顺序

一个函数还可以同时定义多个装饰器,比如:

@a
@b
@c
def f ():
    pass
Nach dem Login kopieren

它的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于

f = a(b(c(f)))
Nach dem Login kopieren

zhijun liu首发于知乎专栏Python之禅,原文地址:https://zhuanlan.zhihu.com/p/24900548

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
1 Monate vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
1 Monate vor By 尊渡假赌尊渡假赌尊渡假赌
Will R.E.P.O. Crossplay haben?
1 Monate vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Wie löste ich das Problem der Berechtigungen beim Betrachten der Python -Version in Linux Terminal? Wie löste ich das Problem der Berechtigungen beim Betrachten der Python -Version in Linux Terminal? Apr 01, 2025 pm 05:09 PM

Lösung für Erlaubnisprobleme beim Betrachten der Python -Version in Linux Terminal Wenn Sie versuchen, die Python -Version in Linux Terminal anzuzeigen, geben Sie Python ein ...

Wie kann ich die gesamte Spalte eines Datenrahmens effizient in einen anderen Datenrahmen mit verschiedenen Strukturen in Python kopieren? Wie kann ich die gesamte Spalte eines Datenrahmens effizient in einen anderen Datenrahmen mit verschiedenen Strukturen in Python kopieren? Apr 01, 2025 pm 11:15 PM

Bei der Verwendung von Pythons Pandas -Bibliothek ist das Kopieren von ganzen Spalten zwischen zwei Datenrahmen mit unterschiedlichen Strukturen ein häufiges Problem. Angenommen, wir haben zwei Daten ...

Wie lehre ich innerhalb von 10 Stunden die Grundlagen für Computer-Anfänger-Programmierbasis in Projekt- und problemorientierten Methoden? Wie lehre ich innerhalb von 10 Stunden die Grundlagen für Computer-Anfänger-Programmierbasis in Projekt- und problemorientierten Methoden? Apr 02, 2025 am 07:18 AM

Wie lehre ich innerhalb von 10 Stunden die Grundlagen für Computer -Anfänger für Programmierungen? Wenn Sie nur 10 Stunden Zeit haben, um Computer -Anfänger zu unterrichten, was Sie mit Programmierkenntnissen unterrichten möchten, was würden Sie dann beibringen ...

Wie kann man vom Browser vermeiden, wenn man überall Fiddler für das Lesen des Menschen in der Mitte verwendet? Wie kann man vom Browser vermeiden, wenn man überall Fiddler für das Lesen des Menschen in der Mitte verwendet? Apr 02, 2025 am 07:15 AM

Wie kann man nicht erkannt werden, wenn Sie Fiddlereverywhere für Man-in-the-Middle-Lesungen verwenden, wenn Sie FiddLereverywhere verwenden ...

Was sind reguläre Ausdrücke? Was sind reguläre Ausdrücke? Mar 20, 2025 pm 06:25 PM

Regelmäßige Ausdrücke sind leistungsstarke Tools für Musteranpassung und Textmanipulation in der Programmierung, wodurch die Effizienz bei der Textverarbeitung in verschiedenen Anwendungen verbessert wird.

Was sind einige beliebte Python -Bibliotheken und ihre Verwendung? Was sind einige beliebte Python -Bibliotheken und ihre Verwendung? Mar 21, 2025 pm 06:46 PM

In dem Artikel werden beliebte Python-Bibliotheken wie Numpy, Pandas, Matplotlib, Scikit-Learn, TensorFlow, Django, Flask und Anfragen erörtert, die ihre Verwendung in wissenschaftlichen Computing, Datenanalyse, Visualisierung, maschinellem Lernen, Webentwicklung und h beschreiben

Wie hört Uvicorn kontinuierlich auf HTTP -Anfragen ohne Serving_forver () an? Wie hört Uvicorn kontinuierlich auf HTTP -Anfragen ohne Serving_forver () an? Apr 01, 2025 pm 10:51 PM

Wie hört Uvicorn kontinuierlich auf HTTP -Anfragen an? Uvicorn ist ein leichter Webserver, der auf ASGI basiert. Eine seiner Kernfunktionen ist es, auf HTTP -Anfragen zu hören und weiterzumachen ...

Wie erstelle ich dynamisch ein Objekt über eine Zeichenfolge und rufe seine Methoden in Python auf? Wie erstelle ich dynamisch ein Objekt über eine Zeichenfolge und rufe seine Methoden in Python auf? Apr 01, 2025 pm 11:18 PM

Wie erstellt in Python ein Objekt dynamisch über eine Zeichenfolge und ruft seine Methoden auf? Dies ist eine häufige Programmieranforderung, insbesondere wenn sie konfiguriert oder ausgeführt werden muss ...

See all articles