Dekoraatorid

Pythoni dekoraatorid on funktsioonid, mis võimaldavad esialgsesse funktsiooni lisada täiendusi, ilma selle kuju muutmata.

Dekoraatoreid on võimalik koostada järgnevalt:

# Decorator for a function with no arguments
def example_dec_noarg(function):
    def wrapper():
        # Code goes here, do something with param function
    return wrapper

# Decorator for a function with one argument
def example_dec_arg(function):
    def wrapper(arg):
        # Code goes here, do something with param function, arg
    return wrapper

# Decorator for a function with any amount of arguments
def example_dec_multi_arg(function):
    def wrapper(*args, **kwargs):
        # Code goes here, do something with param function, args, kwargs
    return wrapper

# Decorator with aguments given to itself
def example_dec_arg_itself(*decorator_args):
    def inner(function):
        def wrapper(*agrs, **kwargs): # Arguments optional
            # Code goes here
        return wrapper
    # Code goes here
    # We can implement some functionality here
    # That does things with *decorator_args
    return inner

Dekoraatoreid on võimalik välja kutsuda nii klasside ning meetodite/funktsioonide ees. Samas on ka võimalik funktsioon salvestada eraldi muutujana ning siis see välja kutsuda:

def decorator(function):
    def wrapper():
        # Code goes here
    return wrapper

@decorator
def example_function():
    # Code goes here

@decorator
class Foo:

    @decorator
    def bar():
        # Class method goes here

def method_to_be_wrapped():
    # Code goes here

# Declaring method as variable
# This is a good example on what decorators basically do
wrapped_method = decorator(method_to_be_wrapped())
# Calling out wrapped method
wrapped_method()

Oletame, et meil on mingi osa koodist, mille kiirust me tahame mõõta.

def foo():
    for i in range(100000000):
        pass # Just an empty for loop for example

Üks viis selle tegemiseks, oleks seda käsitsi teha, tuues nimeruumi sisse "time" teek ning salvestada kaks eri-punkti ajas muutujatena ning nende vahe lõpuks välja arvutada.

import time

def foo():
    start = time.time()
    for i in range(100000000):
        pass # Just an empty for loop for example
    end = time.time()
    print(f"Time elapsed: {end - start}")

Kui meil nüüd oleks vaja mitmes erinevas funktsioonis seda teha, tähendaks see palju korduste tekkimist koodi. Selle saab lahendada koostades dekoraatori, mille eesmärgiks oleks saada sisendina funktsioon ning tagastada/prinitida välja, kaua aega läks funktsioonil, et oma ülesanne lõpuni viia.

import time

def timer(func):
    def wrapper():
        start = time.time()
        func()
        end = time.time()
        print(f"Time elapsed: {end - start}")
    return wrapper

@timer
def foo():
    for i in range(100000000):
        pass # Just an empty for loop for example

Nüüd kui funktsioon välja kutsuda, jooksutatakse kood läbi dekoraatori ning ajamõõtmine tehakse selle tasemel. Lisaks nüüd on võimalik hõlpsamalt mitme erineva funktsiooni kiirust mõõta.

@timer
def foo():
    for i in range(100000000):
        pass # Just an empty for loop for example

@timer
def bar():
    # Code goes here

@timer
def foobar():
    # Code goes here

Edasi lugemiseks: https://peps.python.org/pep-0318/