Inhaltsverzeichnis
Narrenlösung
Funktionsumbruch
Allgemeiner Abschluss
Syntaktischer Zucker
Bewertungsdekorateur
Dekorator mit Parametern
智能装饰器
Heim Backend-Entwicklung Python-Tutorial Detaillierte Einführung in die Verwendung von Python-Dekoratoren (Codebeispiele)

Detaillierte Einführung in die Verwendung von Python-Dekoratoren (Codebeispiele)

Feb 25, 2019 am 10:33 AM
decorator python 修饰器 装饰器

Dieser Artikel bietet Ihnen eine detaillierte Einführung (Codebeispiel) für Python-Dekoratoren. Ich hoffe, dass er für Freunde hilfreich ist.

In Python werden Dekoratoren im Allgemeinen verwendet, um Funktionen zu dekorieren, um öffentliche Funktionen zu implementieren und eine Wiederverwendung von Code zu erreichen. Fügen Sie @xxxx vor der Funktionsdefinition hinzu, und dann fügt die Funktion bestimmte Verhaltensweisen ein, was erstaunlich ist! Dies ist jedoch nur syntaktischer Zucker.

Szenario

Angenommen, es gibt einige Arbeitsfunktionen, die Daten unterschiedlich verarbeiten:

def work_bar(data):
    pass


def work_foo(data):
    pass
Nach dem Login kopieren

Wir möchten dies vor/nach dem Funktionsaufruf tun Ausgabeprotokoll, was soll ich tun?

Narrenlösung

logging.info('begin call work_bar')
work_bar(1)
logging.info('call work_bar done')
Nach dem Login kopieren

Was passiert, wenn es mehrere Codeaufrufe gibt? Es macht mir Angst, wenn ich nur daran denke!

Funktionsumbruch

Die Lösung des Narren ist nichts anderes als zu viel redundanter Code, und Sie müssen ihn für jeden Funktionsaufruf erneut schreibenlogging. Dieser Teil der redundanten Logik kann in eine neue Funktion gekapselt werden:

def smart_work_bar(data):
    logging.info('begin call: work_bar')
    work_bar(data)
    logging.info('call doen: work_bar')
Nach dem Login kopieren

Auf diese Weise rufen Sie einfach jedes Mal smart_work_bar auf:

smart_work_bar(1)

# ...

smart_work_bar(some_data)
Nach dem Login kopieren

Allgemeiner Abschluss

sieht aus Es ist perfekt... Wenn work_foo jedoch auch das gleiche Bedürfnis hat, müssen wir es dann noch einmal umsetzen smart_work_foo? Das ist offensichtlich unwissenschaftlich!

Keine Sorge, wir können Abschlüsse verwenden:

def log_call(func):
    def proxy(*args, **kwargs):
        logging.info('begin call: {name}'.format(name=func.func_name))
        result = func(*args, **kwargs)
        logging.info('call done: {name}'.format(name=func.func_name))
        return result
    return proxy
Nach dem Login kopieren

Diese Funktion empfängt ein Funktionsobjekt (Proxy-Funktion) als Parameter und gibt eine Proxy-Funktion zurück. Beim Aufruf der Proxy-Funktion wird zuerst das Protokoll ausgegeben, dann wird die Proxy-Funktion aufgerufen, nach Abschluss des Aufrufs wird das Protokoll ausgegeben und schließlich wird das Aufrufergebnis zurückgegeben. Erreicht es auf diese Weise nicht den Zweck der Verallgemeinerung? ——Für jede Proxy-Funktion func kann log_call problemlos gehandhabt werden. In Zeile

von

smart_work_bar = log_call(work_bar)
smart_work_foo = log_call(work_foo)

smart_work_bar(1)
smart_work_foo(1)

# ...

smart_work_bar(some_data)
smart_work_foo(some_data)
Nach dem Login kopieren
1 empfängt log_call den Parameter work_bar, gibt eine Proxy-Funktion proxy zurück und weist sie smart_work_bar zu. Rufen Sie in der Zeile 4 smart_work_bar auf, was die Proxy-Funktion proxy ist, geben Sie zuerst das Protokoll aus, rufen Sie dann func auf, was work_bar ist, und geben Sie schließlich das Protokoll aus. Beachten Sie, dass func in der Proxy-Funktion eng mit dem übergebenen work_bar-Objekt verknüpft ist. Dies ist der -Abschluss .

Eine weitere Sache: Sie können den Proxy-Funktionsnamen mit smart_ überschreiben, um einen neuen Namen zu erhalten:

work_bar = log_call(work_bar)
work_foo = log_call(work_foo)

work_bar(1)
work_foo(1)
Nach dem Login kopieren

Syntaktischer Zucker

Werfen wir zunächst einen Blick auf den folgenden Code:

def work_bar(data):
    pass
work_bar = log_call(work_bar)


def work_foo(data):
    pass
work_foo = log_call(work_foo)
Nach dem Login kopieren

Obwohl der Code nicht mehr redundant ist, ist er immer noch nicht intuitiv genug. Zu diesem Zeitpunkt kommt syntaktischer Zucker~~~

@log_call
def work_bar(data):
    pass
Nach dem Login kopieren

Achten Sie also auf eine Sache (Hervorhebung hinzugefügt), die Rolle von @log_call besteht hier nur darin, das Python zu sagen Compiler zum Einfügen von Codework_bar = log_call(work_bar).

Bewertungsdekorateur

Raten Sie zunächst, was der Dekorateur eval_now macht?

def eval_now(func):
    return func()
Nach dem Login kopieren

Es sieht seltsam aus. Es ist keine Proxy-Funktion definiert. Zählt es als Dekorator?

@eval_now
def foo():
    return 1

print foo
Nach dem Login kopieren

Dieser Code gibt 1 aus, um die Funktion aufzurufen und auszuwerten. Was nützt es also? Kannst du nicht einfach foo = 1 direkt schreiben? In diesem einfachen Beispiel ist es natürlich möglich, so zu schreiben. Schauen wir uns ein komplexeres Beispiel an: Initialisieren Sie ein Protokollobjekt:

# some other code before...

# log format
formatter = logging.Formatter(
    '[%(asctime)s] %(process)5d %(levelname) 8s - %(message)s',
    '%Y-%m-%d %H:%M:%S',
)

# stdout handler
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(formatter)
stdout_handler.setLevel(logging.DEBUG)

# stderr handler
stderr_handler = logging.StreamHandler(sys.stderr)
stderr_handler.setFormatter(formatter)
stderr_handler.setLevel(logging.ERROR)

# logger object
logger = logging.Logger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(stdout_handler)
logger.addHandler(stderr_handler)

# again some other code after...
Nach dem Login kopieren

Verwenden Sie die eval_now-Methode:

# some other code before...

@eval_now
def logger():
    # log format
    formatter = logging.Formatter(
        '[%(asctime)s] %(process)5d %(levelname) 8s - %(message)s',
        '%Y-%m-%d %H:%M:%S',
    )

    # stdout handler
    stdout_handler = logging.StreamHandler(sys.stdout)
    stdout_handler.setFormatter(formatter)
    stdout_handler.setLevel(logging.DEBUG)

    # stderr handler
    stderr_handler = logging.StreamHandler(sys.stderr)
    stderr_handler.setFormatter(formatter)
    stderr_handler.setLevel(logging.ERROR)

    # logger object
    logger = logging.Logger(__name__)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(stdout_handler)
    logger.addHandler(stderr_handler)

    return logger

# again some other code after...
Nach dem Login kopieren

Der Zweck der beiden Codeteile ist derselbe, letzteres jedoch Offensichtlich klarer und mehr Codeblock-Stil. Noch wichtiger ist, dass Funktionsaufrufe im lokalen Namespace initialisiert werden, um zu verhindern, dass temporäre Variablen (wie formatter usw.) externe Namespaces (wie den globalen) verschmutzen.

Dekorator mit Parametern

Definieren Sie einen Dekorator zum Aufzeichnen langsamer Funktionsaufrufe:

def log_slow_call(func):
    def proxy(*args, **kwargs):
        start_ts = time.time()
        result = func(*args, **kwargs)
        end_ts = time.time()

        seconds = start_ts - end_ts
        if seconds > 1:
        logging.warn('slow call: {name} in {seconds}s'.format(
            name=func.func_name,
            seconds=seconds,
        ))

        return result

    return proxy
Nach dem Login kopieren

Die Zeilen 3 und 5 werden jeweils vor und nach dem Funktionsaufruf abgetastet Aktuelle Uhrzeit, Zeile 7 berechnet die Anrufzeit und gibt ein Warnprotokoll aus, wenn der Anruf länger als eine Sekunde dauert.

@log_slow_call
def sleep_seconds(seconds):
    time.sleep(seconds)

sleep_seconds(0.1)  # 没有日志输出

sleep_seconds(2)    # 输出警告日志
Nach dem Login kopieren

Die Schwellenwerteinstellung hängt jedoch immer von der Situation ab und verschiedene Funktionen können unterschiedliche Werte festlegen. Es wäre schön, wenn es eine Möglichkeit gäbe, den Schwellenwert zu parametrisieren:

def log_slow_call(func, threshold=1):
    def proxy(*args, **kwargs):
        start_ts = time.time()
        result = func(*args, **kwargs)
        end_ts = time.time()

        seconds = start_ts - end_ts
        if seconds > threshold:
        logging.warn('slow call: {name} in {seconds}s'.format(
            name=func.func_name,
            seconds=seconds,
        ))

        return result

    return proxy
Nach dem Login kopieren

Allerdings ruft der @xxxx syntaktische Zucker den Dekorator immer mit der dekorierten Funktion als Parameter auf, was bedeutet, dass es keine Möglichkeit gibt, den zu übergeben threshold Parameter. Was zu tun? ——Verwenden Sie einen Abschluss, um die threshold-Parameter zu kapseln:

def log_slow_call(threshold=1):
    def decorator(func):
        def proxy(*args, **kwargs):
            start_ts = time.time()
            result = func(*args, **kwargs)
            end_ts = time.time()

            seconds = start_ts - end_ts
            if seconds > threshold:
            logging.warn('slow call: {name} in {seconds}s'.format(
                name=func.func_name,
                seconds=seconds,
            ))

            return result

        return proxy

    return decorator


@log_slow_call(threshold=0.5)
def sleep_seconds(seconds):
    time.sleep(seconds)
Nach dem Login kopieren

Auf diese Weise ruft log_slow_call(threshold=0.5) die Rückgabefunktion decorator auf und die Funktion verfügt über eine Abschlussvariable threshold mit dem Wert 0.5. decoratorDekorierensleep_secondsnochmals.

Bei Verwendung des Standardschwellenwerts kann der Funktionsaufruf nicht weggelassen werden:

@log_slow_call()
def sleep_seconds(seconds):
    time.sleep(seconds)
Nach dem Login kopieren

Virgo ist möglicherweise mit dem Klammerpaar in der ersten Zeile unzufrieden, daher kann es wie folgt verbessert werden:

def log_slow_call(func=None, threshold=1):
    def decorator(func):
        def proxy(*args, **kwargs):
            start_ts = time.time()
            result = func(*args, **kwargs)
            end_ts = time.time()

            seconds = start_ts - end_ts
            if seconds > threshold:
            logging.warn('slow call: {name} in {seconds}s'.format(
                name=func.func_name,
                seconds=seconds,
            ))

            return result

        return proxy

    if func is None:
        return decorator
    else:
        return decorator(func)
Nach dem Login kopieren

Diese Schreibmethode ist mit zwei verschiedenen Verwendungen kompatibel: Verwendung A Standardschwellenwert (kein Aufruf); Verwendung B benutzerdefinierter Schwellenwert (mit Aufruf). Bei der Verwendung von

# Case A
@log_slow_call
def sleep_seconds(seconds):
    time.sleep(seconds)


# Case B
@log_slow_call(threshold=0.5)
def sleep_seconds(seconds):
    time.sleep(seconds)
Nach dem Login kopieren

A passiert log_slow_call(sleep_seconds), d ). func

用法B中,先发生的是log_slow_call(threshold=0.5)func参数为空,直接返回新的装饰器decorator,关联闭包变量threshold,值为0.5;然后,decorator再装饰函数sleep_seconds,即decorator(sleep_seconds)。注意到,此时threshold关联的值是0.5,完成定制化。

你可能注意到了,这里最好使用关键字参数这种调用方式——使用位置参数会很丑陋:

# Case B-
@log_slow_call(None, 0.5)
def sleep_seconds(seconds):
    time.sleep(seconds)
Nach dem Login kopieren

当然了,函数调用尽量使用关键字参数是一种极佳实践,含义清晰,在参数很多的情况下更是如此。

智能装饰器

上节介绍的写法,嵌套层次较多,如果每个类似的装饰器都用这种方法实现,还是比较费劲的(脑子不够用),也比较容易出错。

假设有一个智能装饰器smart_decorator,修饰装饰器log_slow_call,便可获得同样的能力。这样,log_slow_call定义将变得更清晰,实现起来也更省力啦:

@smart_decorator
def log_slow_call(func, threshold=1):
    def proxy(*args, **kwargs):
        start_ts = time.time()
        result = func(*args, **kwargs)
        end_ts = time.time()

        seconds = start_ts - end_ts
        if seconds > threshold:
        logging.warn('slow call: {name} in {seconds}s'.format(
            name=func.func_name,
            seconds=seconds,
        ))

        return result

    return proxy
Nach dem Login kopieren

脑洞开完,smart_decorator如何实现呢?其实也简单:

def smart_decorator(decorator):

    def decorator_proxy(func=None, **kwargs):
        if func is not None:
            return decorator(func=func, **kwargs)

        def decorator_proxy(func):
            return decorator(func=func, **kwargs)

        return decorator_proxy

    return decorator_proxy
Nach dem Login kopieren

smart_decorator实现了以后,设想就成立了!这时,log_slow_call,就是decorator_proxy(外层),关联的闭包变量decorator是本节最开始定义的log_slow_call(为了避免歧义,称为real_log_slow_call)。log_slow_call支持以下各种用法:

# Case A
@log_slow_call
def sleep_seconds(seconds):
    time.sleep(seconds)
Nach dem Login kopieren

用法A中,执行的是decorator_proxy(sleep_seconds)(外层),func非空,kwargs为空;直接执行decorator(func=func, **kwargs),即real_log_slow_call(sleep_seconds),结果是关联默认参数的proxy

# Case B
# Same to Case A
@log_slow_call()
def sleep_seconds(seconds):
    time.sleep(seconds)
Nach dem Login kopieren

用法B中,先执行decorator_proxy()funckwargs均为空,返回decorator_proxy对象(内层);再执行decorator_proxy(sleep_seconds)(内层);最后执行decorator(func, **kwargs),等价于real_log_slow_call(sleep_seconds),效果与用法A一致。

# Case C
@log_slow_call(threshold=0.5)
def sleep_seconds(seconds):
    time.sleep(seconds)
Nach dem Login kopieren

用法C中,先执行decorator_proxy(threshold=0.5)func为空但kwargs非空,返回decorator_proxy对象(内层);再执行decorator_proxy(sleep_seconds)(内层);最后执行decorator(sleep_seconds, **kwargs),等价于real_log_slow_call(sleep_seconds, threshold=0.5),阈值实现自定义!

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in die Verwendung von Python-Dekoratoren (Codebeispiele). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

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)

Kann gegen Code in Windows 8 ausgeführt werden Kann gegen Code in Windows 8 ausgeführt werden Apr 15, 2025 pm 07:24 PM

VS -Code kann unter Windows 8 ausgeführt werden, aber die Erfahrung ist möglicherweise nicht großartig. Stellen Sie zunächst sicher, dass das System auf den neuesten Patch aktualisiert wurde, und laden Sie dann das VS -Code -Installationspaket herunter, das der Systemarchitektur entspricht und sie wie aufgefordert installiert. Beachten Sie nach der Installation, dass einige Erweiterungen möglicherweise mit Windows 8 nicht kompatibel sind und nach alternativen Erweiterungen suchen oder neuere Windows -Systeme in einer virtuellen Maschine verwenden müssen. Installieren Sie die erforderlichen Erweiterungen, um zu überprüfen, ob sie ordnungsgemäß funktionieren. Obwohl VS -Code unter Windows 8 möglich ist, wird empfohlen, auf ein neueres Windows -System zu upgraden, um eine bessere Entwicklungserfahrung und Sicherheit zu erzielen.

Ist die VSCODE -Erweiterung bösartig? Ist die VSCODE -Erweiterung bösartig? Apr 15, 2025 pm 07:57 PM

VS -Code -Erweiterungen stellen böswillige Risiken dar, wie das Verstecken von böswilligem Code, das Ausbeutetieren von Schwachstellen und das Masturbieren als legitime Erweiterungen. Zu den Methoden zur Identifizierung böswilliger Erweiterungen gehören: Überprüfung von Verlegern, Lesen von Kommentaren, Überprüfung von Code und Installation mit Vorsicht. Zu den Sicherheitsmaßnahmen gehören auch: Sicherheitsbewusstsein, gute Gewohnheiten, regelmäßige Updates und Antivirensoftware.

So führen Sie Programme in der terminalen VSCODE aus So führen Sie Programme in der terminalen VSCODE aus Apr 15, 2025 pm 06:42 PM

Im VS -Code können Sie das Programm im Terminal in den folgenden Schritten ausführen: Erstellen Sie den Code und öffnen Sie das integrierte Terminal, um sicherzustellen, dass das Codeverzeichnis mit dem Terminal Working -Verzeichnis übereinstimmt. Wählen Sie den Befehl aus, den Befehl ausführen, gemäß der Programmiersprache (z. B. Pythons Python your_file_name.py), um zu überprüfen, ob er erfolgreich ausgeführt wird, und Fehler auflösen. Verwenden Sie den Debugger, um die Debugging -Effizienz zu verbessern.

Wählen Sie zwischen PHP und Python: Ein Leitfaden Wählen Sie zwischen PHP und Python: Ein Leitfaden Apr 18, 2025 am 12:24 AM

PHP eignet sich für Webentwicklung und schnelles Prototyping, und Python eignet sich für Datenwissenschaft und maschinelles Lernen. 1.PHP wird für die dynamische Webentwicklung verwendet, mit einfacher Syntax und für schnelle Entwicklung geeignet. 2. Python hat eine kurze Syntax, ist für mehrere Felder geeignet und ein starkes Bibliotheksökosystem.

PHP und Python: Verschiedene Paradigmen erklärt PHP und Python: Verschiedene Paradigmen erklärt Apr 18, 2025 am 12:26 AM

PHP ist hauptsächlich prozedurale Programmierung, unterstützt aber auch die objektorientierte Programmierung (OOP). Python unterstützt eine Vielzahl von Paradigmen, einschließlich OOP, funktionaler und prozeduraler Programmierung. PHP ist für die Webentwicklung geeignet, und Python eignet sich für eine Vielzahl von Anwendungen wie Datenanalyse und maschinelles Lernen.

Kann Visual Studio -Code in Python verwendet werden Kann Visual Studio -Code in Python verwendet werden Apr 15, 2025 pm 08:18 PM

VS -Code kann zum Schreiben von Python verwendet werden und bietet viele Funktionen, die es zu einem idealen Werkzeug für die Entwicklung von Python -Anwendungen machen. Sie ermöglichen es Benutzern: Installation von Python -Erweiterungen, um Funktionen wie Code -Abschluss, Syntax -Hervorhebung und Debugging zu erhalten. Verwenden Sie den Debugger, um Code Schritt für Schritt zu verfolgen, Fehler zu finden und zu beheben. Integrieren Sie Git für die Versionskontrolle. Verwenden Sie Tools für die Codeformatierung, um die Codekonsistenz aufrechtzuerhalten. Verwenden Sie das Lining -Tool, um potenzielle Probleme im Voraus zu erkennen.

Kann VSCODE für MAC verwendet werden Kann VSCODE für MAC verwendet werden Apr 15, 2025 pm 07:36 PM

VS -Code ist auf Mac verfügbar. Es verfügt über leistungsstarke Erweiterungen, GIT -Integration, Terminal und Debugger und bietet auch eine Fülle von Setup -Optionen. Für besonders große Projekte oder hoch berufliche Entwicklung kann VS -Code jedoch Leistung oder funktionale Einschränkungen aufweisen.

Kann vscode ipynb ausführen Kann vscode ipynb ausführen Apr 15, 2025 pm 07:30 PM

Der Schlüssel zum Ausführen von Jupyter -Notebook im VS -Code liegt darin, sicherzustellen, dass die Python -Umgebung ordnungsgemäß konfiguriert ist, verstehen, dass die Codeausführungsreihenfolge mit der Zellreihenfolge übereinstimmt, und sich der großen Dateien oder externen Bibliotheken bewusst zu sein, die die Leistung beeinflussen können. Die vom VS -Code bereitgestellten Codebetausch- und Debugging -Funktionen können die Codierungseffizienz erheblich verbessern und Fehler verringern.

See all articles