Python Decorators

Ein Python Decorator bietet eine prägnante und wiederverwendbare Möglichkeit, eine Funktion oder eine Klasse zu erweitern. Lesen Sie den begleitenden Artikel Python Decorators: Simple Patterns to Level Up Your Code für praktische Beispiele und Muster.

Einfacher Decorator

Ein Decorator in seiner einfachsten Form ist eine Funktion, die eine andere Funktion als Argument entgegennimmt und einen Wrapper zurückgibt. Das folgende Beispiel zeigt die Erstellung eines Decorators und seine Verwendung.

# Decorator: a function that takes another function and returns a wrapper
def your_decorator(func):
  def wrapper():
    # Do stuff before func...
    print("Before func!")
    func()  # Call the original function
    # Do stuff after func...
    print("After func!")
  return wrapper  # Return the wrapper function

# @your_decorator is syntactic sugar for: foo = your_decorator(foo)
@your_decorator
def foo():
  print("Hello World!")

foo()  # Calls wrapper, which calls foo with extra behavior
Before func!
Hello World!
After func!
Quiz

Melden Sie sich an, um dieses Quiz zu beantworten und Ihren Lernfortschritt zu verfolgen

What is a decorator in Python?
A. A function that takes another function and returns a wrapper function
B. A special type of class
C. A built-in Python keyword
D. A way to delete functions

Decorator für eine Funktion mit Parametern

# Decorator that works with functions that have parameters
def your_decorator(func):
  def wrapper(*args,**kwargs):  # Accept any arguments
    # Do stuff before func...
    print("Before func!")
    func(*args,**kwargs)  # Pass arguments to original function
    # Do stuff after func...
    print("After func!")
  return wrapper

@your_decorator
def foo(bar):
  print("My name is " + bar)

foo("Jack")  # Arguments are passed through wrapper
Before func!
My name is Jack
After func!

Vorlage für einen Basis-Decorator

Diese Vorlage ist für die meisten Anwendungsfälle von Decorators nützlich. Sie ist gültig für Funktionen mit oder ohne Parameter und mit oder ohne Rückgabewert.

import functools

# Best practice decorator template: preserves function metadata and return value
def your_decorator(func):
  @functools.wraps(func)  # Preserves function name, docstring, etc.
  def wrapper(*args,**kwargs):
    # Do stuff before func...
    result = func(*args,**kwargs)  # Call function and capture return value
    # Do stuff after func..
    return result  # Return the original function's return value
  return wrapper
Quiz

Melden Sie sich an, um dieses Quiz zu beantworten und Ihren Lernfortschritt zu verfolgen

What does @functools.wraps(func) do in a decorator?
A. Makes the decorator execute faster
B. Preserves the original function's metadata (name, docstring, etc.)
C. Prevents the function from being called
D. Converts the function to a class

Decorator mit Parametern

Sie können auch Parameter für den Decorator definieren, die dieser verwenden soll.

import functools

# Decorator factory: returns a decorator based on parameters
def your_decorator(arg):
  def decorator(func):
    @functools.wraps(func)  # Preserve function metadata
    def wrapper(*args,**kwargs):
      # Do stuff before func possibly using arg...
      result = func(*args,**kwargs)
      # Do stuff after func possibly using arg...
      return result
    return wrapper
  return decorator  # Return the actual decorator function

Um diesen Decorator zu verwenden:

# Using decorator with parameters: @your_decorator(arg='x') calls your_decorator('x')
# which returns a decorator that is then applied to foo
@your_decorator(arg = 'x')
def foo(bar):
  return bar

Klassenbasierte Decorators

Um eine Klassenmethode zu dekorieren, müssen Sie den Decorator innerhalb der Klasse definieren. Wenn der Methode nur das implizite Argument self übergeben wird, ohne zusätzliche explizite Argumente, müssen Sie einen separaten Decorator für nur diese Methoden ohne zusätzliche Argumente erstellen. Ein Beispiel hierfür, unten gezeigt, ist, wenn Sie Ausnahmen auf eine bestimmte Weise abfangen und ausgeben möchten.

# Class method decorator: defined within the class
class DecorateMyMethod:

  # Static method decorator for methods with only 'self' parameter
  def decorator_for_class_method_with_no_args(method):
    def wrapper_for_class_method(self):  # Only takes self
      try:
        return method(self)  # Call original method
      except Exception as e:
        print("\nWARNING: Please make note of the following:\n")
        print(e)
    return wrapper_for_class_method

  def __init__(self,succeed:bool):
    self.succeed = succeed

  @decorator_for_class_method_with_no_args
  def class_action(self):
    if self.succeed:
      print("You succeeded by choice.")
    else:
      raise Exception("Epic fail of your own creation.")

test_succeed = DecorateMyMethod(True)
test_succeed.class_action()
You succeeded by choice.
test_fail = DecorateMyMethod(False)
test_fail.class_action()
Exception: Epic fail of your own creation.

Ein Decorator kann auch als Klasse anstelle einer Methode definiert werden. Dies ist nützlich, um einen Zustand beizubehalten und zu aktualisieren, wie im folgenden Beispiel, in dem wir die Anzahl der Aufrufe einer Methode zählen:

# Class-based decorator: maintains state between calls
class CountCallNumber:

  def __init__(self, func):
    self.func = func  # Store the function to decorate
    self.call_number = 0  # Initialize call counter

  def __call__(self, *args, **kwargs):  # Makes instance callable
    self.call_number += 1  # Increment counter
    print("This is execution number " + str(self.call_number))
    return self.func(*args, **kwargs)  # Call original function

@CountCallNumber  # Creates instance of CountCallNumber
def say_hi(name):
  print("Hi! My name is " + name)

say_hi("Jack")  # Calls CountCallNumber.__call__()
This is execution number 1
Hi! My name is Jack
say_hi("James")
This is execution number 2
Hi! My name is James

Count Example

This count example is inspired by Patrick Loeber's YouTube tutorial.