Magyarázat (példákkal és használati esetekkel)

A Python dekorátorok hihetetlenül hasznos konstrukciók a Pythonban. A Pythonban a dekorátorok használatával módosíthatjuk egy függvény viselkedését úgy, hogy becsomagoljuk egy másik függvénybe. A dekorátorok lehetővé teszik számunkra, hogy tisztább kódokat írjunk, és megosszuk a funkciókat. Ez a cikk nemcsak a dekorátorok használatáról, hanem azok létrehozásáról is szól.

Feltétel tudás

A lakberendezők témája Pythonban némi háttérismeretet igényel. Az alábbiakban felsoroltam néhány fogalmat, amelyeket már ismernie kell, hogy megértse ezt az oktatóanyagot. Olyan forrásokat is linkeltem, ahol szükség esetén felfrissítheti a fogalmakat.

Alap Python

Ez a téma egy középhaladóbb/haladóbb téma. Ennek eredményeként, mielőtt megpróbálná megtanulni, már ismernie kell a Python alapjait, például az adattípusokat, függvényeket, objektumokat és osztályokat.

Meg kell értenie néhány objektum-orientált fogalmat is, például a gettereket, a settereket és a konstruktorokat. Ha nem ismeri a Python programozási nyelvet, itt talál néhány forrást a kezdéshez.

A funkciók első osztályú állampolgárok

Az alap Python mellett ezzel a fejlettebb Python koncepcióval is tisztában kell lennie. A függvények, és nagyjából minden más Pythonban olyan objektumok, mint az int vagy a string. Mivel ezek tárgyak, néhány dolgot megtehet velük, nevezetesen:

  • Egy függvényt argumentumként adhat át egy másik függvénynek, ugyanúgy, mint egy karakterláncot vagy int függvény argumentumként.
  • A függvényeket más függvények is visszaadhatják, ahogyan te más karakterlánc- vagy int-értékeket adnál vissza.
  • A függvények változókban tárolhatók

Valójában az egyetlen különbség a funkcionális objektumok és más objektumok között az, hogy a funkcionális objektumok tartalmazzák a __call__() mágikus metódust.

Remélhetőleg ezen a ponton elégedett az előfeltételekkel. Elkezdhetjük tárgyalni a fő témát.

Mi az a Python dekorátor?

A Python-dekorátor egyszerűen egy függvény, amely argumentumként vesz fel egy függvényt, és visszaadja a bevitt függvény módosított változatát. Más szóval, a foo függvény dekorátor, ha argumentumként beveszi a függvénysort. és egy másik baz függvényt ad vissza.

A baz függvény a bar módosítása abban az értelemben, hogy a baz törzsében van egy hívás a függvénysávra. Azonban a hívás előtt és után a baz bármire képes. Ez egy falat volt; itt van néhány kód a helyzet illusztrálására:

# Foo is a decorator, it takes in another function, bar as an argument
def foo(bar):

    # Here we create baz, a modified version of bar
    # baz will call bar but can do anything before and after the function call
    def baz():

        # Before calling bar, we print something
        print("Something")

        # Then we run bar by making a function call
        bar()

        # Then we print something else after running bar
        print("Something else")

    # Lastly, foo returns baz, a modified version of bar
    return baz

Hogyan készítsünk dekorátort Pythonban?

Annak szemléltetésére, hogy a dekorátorokat hogyan készítik és használják a Pythonban, ezt egy egyszerű példával fogom illusztrálni. Ebben a példában létrehozunk egy logger-dekorátor függvényt, amely minden egyes futáskor naplózza a díszített függvény nevét.

  A LinkedIn által Önről gyűjtött adatok kezelése

A kezdéshez létrehoztuk a dekoratőr funkciót. A lakberendező érvként veszi a funkciót. A func az a funkció, amelyet díszítünk.

def create_logger(func):
    # The function body goes here

A dekorátor funkción belül létrehozzuk a módosított függvényünket, amely a func futtatása előtt naplózza a func nevét.

# Inside create_logger
def modified_func():
    print("Calling: ", func.__name__)
    func()

Ezután a create_logger függvény visszaadja a módosított függvényt. Ennek eredményeként a teljes create_logger függvényünk így fog kinézni:

def create_logger(func):
    def modified_func():
        print("Calling: ", func.__name__)
        func()

    return modified_function

Elkészültünk a dekorátor elkészítésével. A create_logger függvény egy egyszerű példa a dekorátor függvényre. Beveszi a func-ot, amely az általunk díszített függvény, és visszaad egy másik függvényt, a modified_func-ot. A modified_func először naplózza a func nevét, mielőtt futtatná a func-ot.

Hogyan használjunk dekorátorokat a Pythonban

A dekorátor használatához a @ szintaxist használjuk, így:

@create_logger
def say_hello():
    print("Hello, World!")

Most már meghívhatjuk a say_hello()-t a szkriptünkben, és a kimenetnek a következő szövegnek kell lennie:

Calling:  say_hello
"Hello, World"

De mit csinál a @create_logger? Nos, ez a dekorátort alkalmazza a say_hello függvényünkhöz. Hogy jobban megértsük, mit csinál, a közvetlenül e bekezdés alatti kód ugyanazt az eredményt érné el, mintha a @create_loggert a say_hello elé helyezné.

def say_hello():
    print("Hello, World!")

say_hello = create_logger(say_hello)

Más szóval, a díszítők Pythonban használatának egyik módja az, hogy kifejezetten meghívjuk a funkcióban áthaladó dekorátort, ahogy a fenti kódban tettük. A másik és tömörebb módja a @ szintaxis használata.

Ebben a részben bemutattuk, hogyan készítsünk Python-dekorátorokat.

Kicsit bonyolultabb példák

A fenti példa egy egyszerű eset volt. Vannak kicsit bonyolultabb példák is, például amikor az általunk díszített függvény érveket vesz fel. Egy másik bonyolultabb helyzet az, amikor egy egész osztályt fel akarsz díszíteni. Itt mindkét helyzetet kitérek.

Amikor a függvény argumentumokat vesz fel

Amikor a díszített függvény argumentumokat vesz fel, a módosított függvénynek fogadnia kell az argumentumokat, és át kell adnia azokat, amikor végül meghívja a módosítatlan függvényt. Ha ez zavaróan hangzik, hadd magyarázzam el a foo-bar kifejezésekkel.

Emlékezzünk vissza, hogy a foo a dekorátor funkció, a bar az a funkció, amelyet mi díszítünk, és a baz a díszített bár. Ebben az esetben a bar beveszi az érveket, és átadja őket baznak a baz hívása során. Íme egy kódpélda a koncepció megszilárdításához:

def foo(bar):
    def baz(*args, **kwargs):
        # You can do something here
        ___
        # Then we make the call to bar, passing in args and kwargs
        bar(*args, **kwargs)
        # You can also do something here
        ___

    return baz

Ha az *argok és a **kwargok ismeretlennek tűnnek; ezek egyszerűen mutatók a pozíció-, illetve kulcsszóargumentumra.

Fontos megjegyezni, hogy a baz hozzáfér az argumentumokhoz, és ezért bizonyos mértékig ellenőrizheti az argumentumokat a bar meghívása előtt.

Példa erre, ha lenne egy dekorátor függvényünk, a biztosíték_string, amely biztosítja, hogy az általa díszített függvénynek átadott argumentum egy karakterlánc legyen; a következőképpen valósítanánk meg:

def ensure_string(func):
    def decorated_func(text):
        if type(text) is not str:
             raise TypeError('argument to ' + func.__name__ + ' must be a string.')
        else:
             func(text)

    return decorated_func

A say_hello függvényt így díszíthetjük:

@ensure_string
def say_hello(name):
    print('Hello', name)

Ezután tesztelhetjük a kódot a következővel:

say_hello('John') # Should run just fine
say_hello(3) # Should throw an exception

És a következő kimenetet kell produkálnia:

Hello John
Traceback (most recent call last):
   File "/home/anesu/Documents/python-tutorial/./decorators.py", line 20, in <module> say hello(3) # should throw an exception
   File "/home/anesu/Documents/python-tu$ ./decorators.pytorial/./decorators.py", line 7, in decorated_func raise TypeError('argument to + func._name_ + must be a string.')
TypeError: argument to say hello must be a string. $0

Ahogy az várható volt, a forgatókönyvnek sikerült kinyomtatnia a „Hello John”-t, mert a „John” egy karakterlánc. Kivételt dobott a „Hello 3” kinyomtatásánál, mert a „3” nem karakterlánc volt. A secure_string dekorátor használható bármely karakterláncot igénylő függvény argumentumainak érvényesítésére.

  9 legjobb JBoss megfigyelő eszköz

Osztály díszítése

A pusztán díszítő funkciókon kívül órákat is tudunk díszíteni. Amikor díszítőt ad hozzá egy osztályhoz, a díszített metódus lecseréli az osztály konstruktor/kezdeményező metódusát (__init__).

Visszatérve a foo-barhoz, tegyük fel, hogy a foo a mi dekorátorunk, és a Bar az az osztály, amelyet díszítünk, akkor a foo fogja díszíteni a Bart.__init__. Ez akkor lesz hasznos, ha bármit meg akarunk tenni a Bar típusú objektumok példányosítása előtt.

Ez azt jelenti, hogy a következő kód

def foo(func):
    def new_func(*args, **kwargs):
        print('Doing some stuff before instantiation')
        func(*args, **kwargs)

    return new_func

@foo
class Bar:
    def __init__(self):
        print("In initiator")

Egyenértékű a

def foo(func):
    def new_func(*args, **kwargs):
        print('Doing some stuff before instantiation')
        func(*args, **kwargs)

    return new_func

class Bar:
    def __init__(self):
        print("In initiator")


Bar.__init__ = foo(Bar.__init__)

Valójában egy Bar osztályú objektum példányosítása, amely a két módszer valamelyikével van definiálva, ugyanazt a kimenetet adja:

Doing some stuff before instantiation
In initiator

Példa dekorátorok Pythonban

Bár Ön meghatározhatja saját dekorátorait, vannak olyanok, amelyek már be vannak építve a Pythonba. Íme néhány gyakori dekorátor, amellyel a Pythonban találkozhat:

@staticmethod

A statikus metódust egy osztályon használják annak jelzésére, hogy az általa díszített metódus statikus metódus. A statikus metódusok olyan módszerek, amelyek az osztály példányosítása nélkül futhatnak. A következő kódpéldában egy Dog osztályt hozunk létre statikus metódus bark segítségével.

class Dog:
    @staticmethod
    def bark():
        print('Woof, woof!')

Most a kéregmódszert így lehet elérni:

Dog.bark()

És a kód futtatása a következő kimenetet eredményezi:

Woof, woof!

Ahogy a Dekorátorok használata című részben említettem, a dekorátorok kétféleképpen használhatók. A @ szintaxis a tömörebb, mivel az egyik a kettő közül. A másik módszer a dekorátor függvény meghívása, argumentumként átadva a díszíteni kívánt függvényt. A fenti kód jelentése ugyanaz, mint az alábbi kód:

class Dog:
    def bark():
        print('Woof, woof!')

Dog.bark = staticmethod(Dog.bark)

A kéregmódszert pedig továbbra is ugyanúgy használhatjuk

Dog.bark()

És ugyanazt a kimenetet produkálná

Woof, woof!

Amint látja, az első módszer tisztább, és sokkal nyilvánvalóbb, hogy a függvény statikus függvény, mielőtt elkezdte volna olvasni a kódot. Ennek eredményeként a többi példában az első módszert fogom használni. De ne feledje, a második módszer alternatíva.

@classmethod

Ez a díszítő arra szolgál, hogy jelezze, hogy az általa díszített metódus osztálymetódus. Az osztálymetódusok abban hasonlítanak a statikus metódusokhoz, hogy mindkettő nem igényli az osztály példányosítását, mielőtt meghívható lenne.

  Mi az a hálózati késleltetés és hogyan javítható? [2022]

A fő különbség azonban az, hogy az osztálymetódusok hozzáférnek az osztályattribútumokhoz, míg a statikus módszerek nem. Ennek az az oka, hogy a Python automatikusan átadja az osztályt első argumentumként egy osztálymetódusnak, amikor azt meghívják. Osztálymetódus létrehozásához Pythonban használhatjuk az osztálymetódus díszítőt.

class Dog:
    @classmethod
    def what_are_you(cls):
        print("I am a " + cls.__name__ + "!")

A kód futtatásához egyszerűen meghívjuk a metódust az osztály példányosítása nélkül:

Dog.what_are_you()

A kimenet pedig:

I am a Dog!

@ingatlan

A tulajdonságdekorátor egy metódus tulajdonságbeállítóként való címkézésére szolgál. Visszatérve a Kutya példához, hozzunk létre egy metódust, amely lekéri a kutya nevét.

class Dog:
    # Creating a constructor method that takes in the dog's name
    def __init__(self, name):

         # Creating a private property name
         # The double underscores make the attribute private
         self.__name = name

    
    @property
    def name(self):
        return self.__name

Most már normális tulajdonként hozzáférhetünk a kutya nevéhez,

# Creating an instance of the class
foo = Dog('foo')

# Accessing the name property
print("The dog's name is:", foo.name)

És a kód futtatásának eredménye az lenne

The dog's name is: foo

@property.setter

A property.setter dekorátor segítségével setter metódust hozunk létre ingatlanjaink számára. A @property.setter dekorátor használatához le kell cserélni a tulajdonságot a tulajdonság nevére, amelyhez beállítót hoz létre. Például, ha létrehoz egy beállítót a foo tulajdonság metódusához, akkor a dekorátor a @foo.setter lesz. Íme egy kutya példa szemléltetésül:

class Dog:
    # Creating a constructor method that takes in the dog's name
    def __init__(self, name):

         # Creating a private property name
         # The double underscores make the attribute private
         self.__name = name

    
    @property
    def name(self):
        return self.__name

    # Creating a setter for our name property
    @name.setter
    def name(self, new_name):
        self.__name = new_name

A setter teszteléséhez a következő kódot használhatjuk:

# Creating a new dog
foo = Dog('foo')

# Changing the dog's name
foo.name="bar"

# Printing the dog's name to the screen
print("The dog's new name is:", foo.name)

A kód futtatása a következő kimenetet eredményezi:

The dogs's new name is: bar

A dekorátorok jelentősége a Pythonban

Most, hogy megismertük a dekorátorokat, és láthatott néhány példát a lakberendezőkre, megvitathatjuk, miért fontosak a lakberendezők a Pythonban. A dekorátorok több okból is fontosak. Ezek közül néhányat az alábbiakban sorolok fel:

  • Lehetővé teszik a kód újrafelhasználását: A fenti naplózási példában a @create_loggert bármilyen funkcióhoz használhatjuk. Ez lehetővé teszi számunkra, hogy minden függvényünkhöz naplózási funkcionalitást adjunk anélkül, hogy manuálisan megírnánk az egyes funkciókhoz.
  • Lehetővé teszik moduláris kód írását: Ismét visszatérve a naplózási példához, a dekorátorokkal elválaszthatja az alapfunkciót, jelen esetben a say_hello-t a többi szükséges funkciótól, jelen esetben a naplózástól.
  • Javítják a keretrendszereket és a könyvtárakat: A dekorátorokat széles körben használják a Python-keretrendszerekben és -könyvtárakban, hogy további funkciókat biztosítsanak. Például az olyan webes keretrendszerekben, mint a Flask vagy a Django, a dekorátorokat útvonalak meghatározására, hitelesítés kezelésére vagy köztes szoftver alkalmazására használják bizonyos nézetekre.

Végső szavak

A dekorátorok hihetetlenül hasznosak; segítségével bővítheti a funkciókat anélkül, hogy megváltoztatná a funkcionalitásukat. Ez akkor hasznos, ha szeretné időzíteni a függvények teljesítményét, naplózni, amikor egy függvény meghívódik, érvényesíteni szeretné az argumentumokat a függvény meghívása előtt, vagy ellenőrizni szeretné az engedélyeket, mielőtt egy függvény futna. Ha megérti a lakberendezőket, tisztább módon tud kódot írni.

Ezután érdemes elolvasni cikkeinket a sorokról és a cURL használatáról a Pythonban.