Skip to content

Dekoratoriai II

DonatasNoreika edited this page Jun 9, 2022 · 7 revisions

prieš tai nagrinėtas dekoratorius (grąžinantis ALL CAPS) turėjo trūkumą - jis priėmė funkcijas tik su fiksuotu kiekiu argumentų(1). Galima rašyti lankstesnius dekoratorius. Tarkime, turime funkcijas, grąžinančias integer reikšmes:

def give_me_10():
    return 10


def multiply(x, y):
    return x * y


def sum_all(*args):
    return sum(args)

Parašykime dekoratorių, kuris priima visas tris funkcijas:

def lyginis_nelyginis(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        if (result % 2) != 0:
            return result, 'nelyginis!'
        return result, 'lyginis!'
    return wrapper

Įvilkime savo f-jas į dekoratorių:

@lyginis_nelyginis
def give_me_10():
    return 10

@lyginis_nelyginis
def multiply(x, y):
    return x * y

@lyginis_nelyginis
def sum_all(*args):
    return sum(args)

atspausdinkime rezultatus:

print(give_me_10())
print(multiply(5,5))
print(sum_all(10, 3, 7, 8))

# (10, 'lyginis!')
# (25, 'nelyginis!')
# (28, 'lyginis!')

Tai yra standartinis universalaus dekoratoriaus rašymo būdas.

Dekoratorių elgsenoje nėra numatytas funkcijos metaduomenų išsaugojimas. Metaduomenų Python'as ieško ne funkcijoje, kurią iškviečiame, o pačiame warapper'yje. Pvz, jeigu modifikuotumėm mūsų dekoratorių tokiu būdu:

def lyginis_nelyginis(func):
    def wrapper(*args, **kwargs):
        """I'm a holly wrapper!!!"""
        result = func(*args, **kwargs)
        if (result % 2) != 0:
            return result, 'nelyginis!'
        return result, 'lyginis!'
    return wrapper

ir kurią nors iš f-jų dokumentuotumėm:

@lyginis_nelyginis
def give_me_10():
    """Grąžina skaičių 10"""
    return 10

pabandę atsispausdinti metaduomenis, gautumėm:

print(give_me_10.__name__)
print(give_me_10.__doc__)

# wrapper
# I'm a holly wrapper!!!

problema sprendžiasi paprastai. Iš functools reikia imporrtuoti wraps ir panaudoti taip:

from functools import wraps

def lyginis_nelyginis(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """I'm a holly wrapper!!!"""
        result = func(*args, **kwargs)
        if (result % 2) != 0:
            return result, 'nelyginis!'
        return result, 'lyginis!'
    return wrapper

kaip matome, wraps yra ne kas kita, o dekoratorius :) šį kartą spausdindami funkcijos metaduomenis, gausime, ko tikėjomės:

print(give_me_10.__name__)
print(give_me_10.__doc__)

# give_me_10
# Grąžina skaičių 10

Kaip sukurti dekoratorių su parametrais:

from time import sleep

def uzvelavimas(laikas):
    def uzvelavimas(fn):
        def wrapper(*args, **kwargs):
            sleep(laikas)
            print(f"Funkcija buvo atidėta {laikas} sekundę(-es)")
            return fn(*args, **kwargs)
        return wrapper
    return uzvelavimas

@uzvelavimas(1)
def for_sukimas(kartai):
    for x in range(kartai):
        print(x, " ", end="")
    print()

@uzvelavimas(3)
def sumavimas(*args):
    print("Skaičių suma:", sum(args))

for_sukimas(10000)

# Funkcija buvo atidėta 1 sekundę(-es)
# 0  1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99  

Dabar skirtingoms funkcijoms galime uždėti dekoratorių su skirtingais argumentais (šiuo atveju - funkcijos bus paleidžiamos su skirtingu užvėlavimu)

Keli dekoratoriai ant funkcijos

Funkcijai galima uždėti ir kelis dekoratorius, pvz.:

def uzvelavimas(laikas):
    def uzvelavimas(fn):
        def wrapper(*args, **kwargs):
            sleep(laikas)
            print(f"Funkcija buvo atidėta {laikas} sekundę(-es)")
            return fn(*args, **kwargs)
        return wrapper
    return uzvelavimas

def args_counter(fn):
    def wrapper(*args, **kwargs):
        print("Args number:", len(args))
        return fn(*args, **kwargs)
    return wrapper

@uzvelavimas(3)
@args_counter
def sumavimas(*args):
    print("Skaičių suma:", sum(args))

sumavimas(50, 30, 20, 15)

# Funkcija buvo atidėta 3 sekundę(-es)
# Args number: 4
# Skaičių suma: 115

Dabar ant funkcijos sumavimas uždėjome du dekoratorius – uzvelavimas (jis užvėlina programos paleidimą 3 sekundėmis) ir args_counter (jis suskaičiuoja funkcijai paduotus argumentus). Paleidus šią funkciją, ji pereina ir per abu dekoratorius ir padaro atitinkamą veiksmą.

Dekoratoriai

Iteratoriai ir generatoriai

RegEx

Pillow

Email

NumPy

Pandas

Seaborn

Mašininis mokymasis

Requests, JSON, API

Web Scraping (Beautiful Soup)

Duomenų bazės

Flask

Django

Django REST

Odoo

Linux

Pabaigimo užduotis: savo programos kūrimas

Clone this wiki locally