Python Decorators: Πώς να το χρησιμοποιήσετε και γιατί;

Ένας διακοσμητής αναλαμβάνει μια λειτουργία, προσθέτει κάποια λειτουργικότητα και την επιστρέφει. Σε αυτό το σεμινάριο, θα μάθετε πώς μπορείτε να δημιουργήσετε έναν διακοσμητή και γιατί πρέπει να το χρησιμοποιήσετε.

Διακοσμητές στο Python

Η Python έχει ένα ενδιαφέρον χαρακτηριστικό που ονομάζεται διακοσμητές για να προσθέσει λειτουργικότητα σε έναν υπάρχοντα κώδικα.

Αυτό ονομάζεται επίσης μεταπρογραμματισμός επειδή ένα μέρος του προγράμματος προσπαθεί να τροποποιήσει ένα άλλο μέρος του προγράμματος τη στιγμή της μεταγλώττισης.

Προαπαιτούμενα για την εκμάθηση διακοσμητών

Για να κατανοήσουμε τους διακοσμητές, πρέπει πρώτα να γνωρίζουμε μερικά βασικά πράγματα στην Python.

Πρέπει να είμαστε άνετοι με το γεγονός ότι τα πάντα στην Python (Ναι! Ακόμα και μαθήματα) είναι αντικείμενα. Τα ονόματα που ορίζουμε είναι απλά αναγνωριστικά που συνδέονται με αυτά τα αντικείμενα. Οι συναρτήσεις δεν αποτελούν εξαιρέσεις, είναι επίσης αντικείμενα (με χαρακτηριστικά). Διάφορα διαφορετικά ονόματα μπορούν να συνδεθούν με το ίδιο αντικείμενο λειτουργίας.

Εδώ είναι ένα παράδειγμα.

 def first(msg): print(msg) first("Hello") second = first second("Hello")

Παραγωγή

 γεια γεια

Όταν εκτελείτε τον κώδικα, και οι δύο λειτουργίες firstκαι secondδίνουν την ίδια έξοδο. Εδώ, τα ονόματα firstκαι secondαναφέρονται στο ίδιο αντικείμενο λειτουργίας.

Τώρα τα πράγματα αρχίζουν να γίνονται πιο περίεργα.

Οι συναρτήσεις μπορούν να μεταβιβαστούν ως ορίσματα σε μια άλλη συνάρτηση.

Αν έχετε χρησιμοποιήσει λειτουργίες όπως map, filterκαι reduceστην Python, τότε γνωρίζετε ήδη γι 'αυτό.

Τέτοιες συναρτήσεις που λαμβάνουν άλλες συναρτήσεις ως ορίσματα ονομάζονται επίσης συναρτήσεις υψηλότερης τάξης . Εδώ είναι ένα παράδειγμα μιας τέτοιας λειτουργίας.

 def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result

Επικαλούμε τη λειτουργία ως εξής.

 >>> operate(inc,3) 4 >>> operate(dec,3) 2

Επιπλέον, μια συνάρτηση μπορεί να επιστρέψει μια άλλη συνάρτηση.

 def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Outputs "Hello" new()

Παραγωγή

 Χαίρετε

Εδώ, is_returned()είναι μια ένθετη συνάρτηση που ορίζεται και επιστρέφεται κάθε φορά που καλούμε is_called().

Τέλος, πρέπει να γνωρίζουμε για το κλείσιμο στο Python.

Επιστροφή στους Διακοσμητές

Οι λειτουργίες και οι μέθοδοι ονομάζονται callable καθώς μπορούν να κληθούν.

Στην πραγματικότητα, κάθε αντικείμενο που εφαρμόζει την ειδική __call__()μέθοδο ονομάζεται callable. Έτσι, με την πιο βασική έννοια, ένας διακοσμητής είναι ένα callable που επιστρέφει ένα callable.

Βασικά, ένας διακοσμητής αναλαμβάνει μια συνάρτηση, προσθέτει κάποια λειτουργικότητα και την επιστρέφει.

 def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary")

Όταν εκτελείτε τους ακόλουθους κωδικούς με κέλυφος,

 >>> ordinary() I am ordinary >>> # let's decorate this ordinary function >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary

Στο παράδειγμα που φαίνεται παραπάνω, make_pretty()είναι διακοσμητής. Στο βήμα ανάθεσης:

 pretty = make_pretty(ordinary)

Η συνάρτηση ordinary()διακοσμήθηκε και η επιστρεφόμενη συνάρτηση έλαβε το όνομα pretty.

Μπορούμε να δούμε ότι η συνάρτηση διακοσμητή πρόσθεσε κάποια νέα λειτουργικότητα στην αρχική λειτουργία. Αυτό είναι παρόμοιο με τη συσκευασία ενός δώρου. Ο διακοσμητής λειτουργεί ως περιτύλιγμα. Η φύση του αντικειμένου που διακοσμήθηκε (το πραγματικό δώρο μέσα) δεν αλλάζει. Αλλά τώρα, φαίνεται όμορφο (από τότε που διακοσμήθηκε).

Γενικά, διακοσμούμε μια συνάρτηση και την εκχωρούμε ξανά ως,

 ordinary = make_pretty(ordinary).

Αυτή είναι μια κοινή κατασκευή και για αυτόν τον λόγο, η Python έχει μια σύνταξη για να την απλοποιήσει.

Μπορούμε να χρησιμοποιήσουμε το @σύμβολο μαζί με το όνομα της συνάρτησης διακοσμητή και να το τοποθετήσουμε πάνω από τον ορισμό της συνάρτησης που πρόκειται να διακοσμηθεί. Για παράδειγμα,

 @make_pretty def ordinary(): print("I am ordinary")

είναι ισοδύναμο με

 def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary)

Αυτή είναι μόνο μια συντακτική ζάχαρη για την εφαρμογή διακοσμητών.

Λειτουργίες διακόσμησης με παράμετροι

Ο παραπάνω διακοσμητής ήταν απλός και λειτούργησε μόνο με λειτουργίες που δεν είχαν παραμέτρους. Τι γίνεται αν είχαμε συναρτήσεις που πήραν παραμέτρους όπως:

 def divide(a, b): return a/b

Αυτή η συνάρτηση έχει δύο παραμέτρους, a και b. Γνωρίζουμε ότι θα δώσει σφάλμα εάν περάσουμε στο b ως 0.

 >>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last):… ZeroDivisionError: division by zero

Τώρα ας κάνουμε έναν διακοσμητή για να ελέγξουμε αυτήν την περίπτωση που θα προκαλέσει το σφάλμα.

 def smart_divide(func): def inner(a, b): print("I am going to divide", a, "and", b) if b == 0: print("Whoops! cannot divide") return return func(a, b) return inner @smart_divide def divide(a, b): print(a/b)

Αυτή η νέα εφαρμογή θα επιστρέψει Noneεάν προκύψει η κατάσταση σφάλματος.

 >>> divide(2,5) I am going to divide 2 and 5 0.4 >>> divide(2,0) I am going to divide 2 and 0 Whoops! cannot divide

Με αυτόν τον τρόπο, μπορούμε να διακοσμήσουμε συναρτήσεις που λαμβάνουν παραμέτρους.

Ένας έντονος παρατηρητής θα παρατηρήσει ότι οι παράμετροι της ένθετης inner()λειτουργίας εντός του διακοσμητή είναι ίδιες με τις παραμέτρους των λειτουργιών που διακοσμεί. Λαμβάνοντας αυτό υπόψη, τώρα μπορούμε να κάνουμε γενικούς διακοσμητές που λειτουργούν με οποιονδήποτε αριθμό παραμέτρων.

In Python, this magic is done as function(*args, **kwargs). In this way, args will be the tuple of positional arguments and kwargs will be the dictionary of keyword arguments. An example of such a decorator will be:

 def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return inner

Chaining Decorators in Python

Multiple decorators can be chained in Python.

This is to say, a function can be decorated multiple times with different (or same) decorators. We simply place the decorators above the desired function.

 def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello")

Output

 ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************

The above syntax of,

 @star @percent def printer(msg): print(msg)

is equivalent to

 def printer(msg): print(msg) printer = star(percent(printer))

The order in which we chain decorators matter. If we had reversed the order as,

 @percent @star def printer(msg): print(msg)

The output would be:

 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

ενδιαφέροντα άρθρα...