Πώς το Zen της Python μπορεί να σας βοηθήσει να γράψετε καλύτερο κώδικα

Θέλετε να γίνετε καλύτεροι στη σύνταξη κώδικα Python; Δείτε πώς το Zen of Python μπορεί να σας βοηθήσει να κάνετε τα πρώτα βήματα προς αυτό.

Η εκμάθηση της Python είναι εξαιρετικά απλή. Αλλά η σύνταξη ιδιωματικού και Pythonic κώδικα που είναι εύκολο να διατηρηθεί μπορεί να είναι δύσκολη — ειδικά για αρχάριους προγραμματιστές. Το PEP-20 παρουσίασε το «The Zen of Python», ένα ποίημα του Tim Peters, που περιγράφει τη σημασία της συγγραφής Pythonic κώδικα που ακολουθεί τις βέλτιστες πρακτικές.

Για να διαβάσετε το Zen της Python, μπορείτε να ξεκινήσετε μια Python REPL και να εκτελέσετε:

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Όπως φαίνεται, οι περισσότεροι από τους αφορισμούς στο Zen of Python είναι αυτονόητοι. Ορισμένοι αφορισμοί πρέπει να συνδυαστούν με τον επόμενο κατά την ερμηνεία, ενώ κάποιοι άλλοι έρχονται σε αντίθεση με έναν προηγούμενο αφορισμό. Ωστόσο, το Zen of Python είναι ένα διασκεδαστικό, συναρπαστικό και πρακτικό ανάγνωσμα!

Πίνακας περιεχομένων

Ερμηνεύοντας το Ζεν της Python

Το Zen of Python προτάθηκε να έχει 20 κατευθυντήριες αρχές για τον προγραμματισμό σε Python. Ωστόσο, υπάρχουν μόνο 19 αφορισμοί μέχρι στιγμής. Ας τους περάσουμε.

Το όμορφο είναι καλύτερο από το άσχημο.

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

  Τι συμβαίνει εάν δεν θυμάστε τον κωδικό πρόσβασης του Gmail;

Το παρακάτω απόσπασμα κώδικα έχει μυρωδιά κώδικα:

def square(num):
    squares = []
    for i in range(num):
        squares.append(i*i)
    return squares

Η λειτουργία:

  • Αρχικοποιεί μια κενή λίστα
  • Έχει ένα βρόχο μέσα στη συνάρτηση που προσαρτά στοιχεία στο τέλος της λίστας και
  • Τέλος επιστρέφει μια λίστα

Αν και αυτό είναι λειτουργικά σωστό – δεν είναι Pythonic – και είναι δύσκολο να διατηρηθεί.

Μπορείτε να το γράψετε πολύ πιο κομψά χρησιμοποιώντας γεννήτριες. Ακολουθεί το ισοδύναμο της συνάρτησης γεννήτριας της παραπάνω συνάρτησης:

def square(num):
    for i in range(num):
        yield i*i

Ή ακόμα καλύτερα, μπορείτε να έχετε την ακόλουθη έκφραση κατανόησης γεννήτριας:

num = ...
squares = (i*i for i in range(num))

Το ρητό είναι καλύτερο από το άρρητο.

Όταν γράφετε κώδικα, μην αφήνετε άλλους προγραμματιστές και χρήστες να μαντεύουν την υπονοούμενη ή προεπιλεγμένη συμπεριφορά του κώδικα. Να είστε σαφείς. Πάρτε το παράδειγμα εισαγωγής χαρακτήρων μπαλαντέρ:

from some_module import * # wildcard import
from some_other_module import *

result = some_function() # where did this come from?

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

from some_module import this_function # explicit import

result = this_function() # we now know.

Το απλό είναι καλύτερο από το σύνθετο.

Αυτός ο αφορισμός δηλώνει ότι πρέπει να διατηρήσουμε τον κώδικα απλό και να αποφύγουμε την περιττή πολυπλοκότητα. Για παράδειγμα: μπορεί να θέλετε να αντιστρέψετε μια συμβολοσειρά και να εφαρμόσετε την ακόλουθη αναδρομική λύση:

def reverse_string(my_string):
  if my_string == "":
    return my_string
  else:
    return reverse_string(my_string[1:]) + my_string[:1]

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

Ακολουθεί η προσέγγιση κοπής χορδών:

>>> rev_string = my_string[::-1]
>>> rev_string
'nohtyP'

Και εδώ είναι η προσέγγιση που χρησιμοποιεί ενσωματωμένες μεθόδους και λειτουργίες:

>>> rev_string = ''.join(reversed(my_string))
>>> rev_string
'nohtyP'

Το σύνθετο είναι καλύτερο από το περίπλοκο.

Τι σημαίνει λοιπόν αυτός ο επόμενος αφορισμός στο Ζεν του Πύθωνα;

Η αντιστροφή συμβολοσειρών στην Python είναι μια εξαιρετικά απλή λειτουργία. Στην πράξη, όμως, ίσως χρειαζόμαστε πιο περίπλοκη λογική. Εδώ είναι ένα αρκετά απλό παράδειγμα:

Ας υποθέσουμε ότι πρέπει να συνδεθείτε σε μια βάση δεδομένων:

  • Θα πρέπει πρώτα να αναλύσετε ένα αρχείο διαμόρφωσης toml—για να ανακτήσετε τις πληροφορίες διαμόρφωσης της βάσης δεδομένων.
  • Θα πρέπει να εγκατασταθεί ο σύνδεσμος βάσης δεδομένων.
  • Στη συνέχεια, μπορείτε να ορίσετε μια συνάρτηση για σύνδεση στη βάση δεδομένων, πρόβλεψη σφαλμάτων σύνδεσης, εφαρμογή διαχείρισης σφαλμάτων και πολλά άλλα.
  • Τέλος, αφού συνδεθείτε με τη βάση δεδομένων, μπορείτε να ρωτήσετε.

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

Το επίπεδο είναι καλύτερο από το φωλιασμένο.

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

  Πώς να αποφύγετε τα Spoilers του Infinity War μέχρι να δείτε την ταινία

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

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

from db_info.config.actions.parse.parse_config import parse_toml # too difficult to parse!
...

from db_config.parse_config import parse_toml # much better!
...

Το αραιό είναι καλύτερο από το πυκνό.

Εάν μόλις ξεκινάτε το ταξίδι προγραμματιστή σας, μπορεί να μπείτε στον πειρασμό να χρησιμοποιήσετε υπερβολικά ορισμένες από τις δυνατότητες της γλώσσας. Οι κατανοήσεις λιστών, για παράδειγμα, είναι Pythonic — αλλά μόνο όταν τις χρησιμοποιείτε όπου χρειάζονται.

Δείτε την παρακάτω κατανόηση:

prices_dict = {'melons':40,'apples':70,'berries':55}
items = [(fruit,price) for fruit in prices_dict.keys() if fruit.startswith('m') for price in prices_dict.values() if price < 50]
print(items)
# Output: [('melons', 40)]

Η κατανόηση της λίστας είναι πολύ πυκνή και δύσκολο να αναλυθεί. Σε αυτήν την περίπτωση, η χρήση ενός ισοδύναμου βρόχου for με όρους θα είναι πιο ευανάγνωστη. Αυτό σημαίνει ότι η κατανόηση είναι δύσκολο να κατανοηθεί. 🙂

Η αναγνωσιμότητα μετράει.

Θα πρέπει πάντα να γράφετε αναγνώσιμο κώδικα. Ακολουθούν μερικοί απλοί τρόποι για τη βελτίωση της αναγνωσιμότητας του κώδικα:

  • Χρήση περιγραφικών ονομάτων μεταβλητών
  • Προσθήκη συμβολοσειρών εγγράφων για συναρτήσεις και κλάσεις
  • Κωδικός σχολιασμού όπου χρειάζεται
  • Προσθήκη υποδείξεων τύπου για ορίσματα και επιστροφή τύπων συναρτήσεων

Οι ειδικές περιπτώσεις δεν είναι αρκετά ιδιαίτερες για να παραβιάσουν τους κανόνες.

Θα πρέπει —όσο είναι δυνατόν— να τηρείτε τους κανόνες της γλώσσας και τις συνιστώμενες βέλτιστες πρακτικές.

Είναι όμως πάντα αυτό εφικτό; Όχι, και γι’ αυτό έχουμε τον επόμενο αφορισμό.

Αν και η πρακτικότητα νικάει την αγνότητα.

Αυτό είναι συνέχεια του προηγούμενου αφορισμού. Αν και συνιστάται να ακολουθείτε τους κανόνες της γλώσσας, σε ορισμένες περιπτώσεις, είναι πολύ καλό να μην ακολουθείτε ορισμένες από τις αρχές.

Τα λάθη δεν πρέπει ποτέ να περνούν σιωπηλά.

Στην Python τα σφάλματα χρόνου εκτέλεσης είναι αρκετά συνηθισμένα. Ως καλή πρακτική, θα πρέπει πάντα να χειρίζεστε τα λάθη και να μην τα αποσιωπάτε ως γρήγορη λύση.

Μπορείτε να προβλέψετε και να εφαρμόσετε τον κατάλληλο χειρισμό σφαλμάτων—για τους διαφορετικούς τύπους σφαλμάτων:

try:  
    # doing this
except ErrorType1:
    # do something
except ErrorType2:
    # do something else
...

Θα πρέπει να αποφεύγετε τις απλές και γενικές εξαιρέσεις. Οι νεότερες εκδόσεις της Python (από την Python 3.11) υποστηρίζουν αλυσίδες εξαιρέσεων και ομάδες εξαιρέσεων για την εκτέλεση πιο εξελιγμένου χειρισμού εξαιρέσεων.

Εκτός αν σιγήσει ρητά.

Αυτό ακολουθεί τον προηγούμενο αφορισμό. Εάν ο σχεδιασμός απαιτεί ή επιτρέπει τη σίγαση του σφάλματος, τότε θα πρέπει να γίνει ρητά.

Για παράδειγμα: κατά τη σύνδεση στη βάση δεδομένων, ενδέχεται να εμφανιστεί Operational Error λόγω μη έγκυρων πληροφοριών διαμόρφωσης. Δοκιμάστε να συνδεθείτε χρησιμοποιώντας προσαρμοσμένες ρυθμίσεις. Σε περίπτωση που υπάρχει Λειτουργικό Σφάλμα, χρησιμοποιήστε την προεπιλεγμένη διαμόρφωση και προσπαθήστε να συνδεθείτε στη βάση δεδομένων.

try:
   # connecting using custom config
except OperationalError:
   # connect using default config

Μπροστά στην ασάφεια, αρνηθείτε τον πειρασμό να μαντέψετε.

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

Πάρτε το ακόλουθο απλό παράδειγμα με μια πλειάδα Booleans:

>>> True, True == (True, True)
(True, False)
>>> True, (True == (True, True))
(True, False)
>>> (True, True) == (True, True)
True

Θα πρέπει να υπάρχει ένας – και κατά προτίμηση μόνο ένας – προφανής τρόπος για να γίνει αυτό.

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

  Πώς να εγκαταστήσετε την Intel ή την AMD CPU στη μητρική σας πλακέτα

Ακόμη και στο απλό παράδειγμα αντιστροφής συμβολοσειράς, εξετάσαμε μια αναδρομική λύση, τον τεμαχισμό συμβολοσειρών και τη μέθοδο join().

Αυτό είναι επίσης ένα εσωτερικό αστείο δεδομένης της ασυνεπούς χρήσης των em-dashes. Γενικά χρησιμοποιούμε παύλες em-παύλες χωρίς προπορευόμενα και υστερούντα κενά. Ή το χρησιμοποιούμε τόσο με τους προπορευόμενους όσο και με τους υστερούντες χώρους.

Εδώ είναι λοιπόν τι μπορούμε να συμπεράνουμε. Ο αφορισμός που τονίζει ότι πρέπει να υπάρχει ένας —και μόνο ένας— Pythonic τρόπος για να κάνεις πράγματα μπορεί να γραφτεί με περισσότερους από δύο τρόπους.

Αν και αυτός ο τρόπος μπορεί να μην είναι προφανής στην αρχή, εκτός αν είστε Ολλανδοί.

Γραπτό σε μια ελαφριά νότα, αυτό αναφέρεται στον Guido Van Rossum, τον δημιουργό του Python (ο οποίος είναι Ολλανδός). Ο (πιο) Python τρόπος για να ολοκληρώσετε μια συγκεκριμένη εργασία – έρχεται φυσικά μόνο για τους δημιουργούς της Python.

Έτσι, για τους προγραμματιστές, απαιτείται εμπειρία —και μάθηση από την εμπειρία— για να γίνουν καλύτεροι στην αξιοποίηση των χαρακτηριστικών της γλώσσας.

Τώρα είναι καλύτερα από ποτέ.

Όπως και με μερικούς άλλους αφορισμούς στο Zen of Python, αυτό μπορεί επίσης να ερμηνευτεί με δύο διαφορετικούς τρόπους.

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

Μια άλλη πιθανή ερμηνεία είναι: ο κώδικας που εκτελείται σε έναν πεπερασμένο αριθμό βημάτων – και τερματίζει – είναι συχνά καλύτερος από τον κώδικα που είναι buggy και κολλάει σε έναν άπειρο βρόχο.

Αν και ποτέ δεν είναι συχνά καλύτερο από τώρα.

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

Η κωδικοποίηση μιας ενότητας —χωρίς να τη σκεφτείς σωστά— με μυρωδιές κώδικα και αντι-μοτίβα είναι κακή ιδέα. Επειδή ένας τέτοιος κώδικας είναι δύσκολο να αναπαραχθεί και να εφαρμοστούν διορθωτικά μέτρα.

Εάν η εφαρμογή είναι δύσκολο να εξηγηθεί, είναι κακή ιδέα.

Οποιαδήποτε λογική -όσο περίπλοκη κι αν είναι- μπορεί πάντα να εφαρμοστεί με μια μορφή που είναι απλή στην εξήγηση και κατανοητή.

Εάν η υλοποίηση είναι δύσκολο να εξηγηθεί, υπάρχει πιθανώς κάποια περιττή πολυπλοκότητα. Ο κώδικας μπορεί να τροποποιηθεί ή να αναδιαμορφωθεί έτσι ώστε να είναι ευκολότερο να ακολουθηθεί.

Εάν η υλοποίηση είναι εύκολο να εξηγηθεί, μπορεί να είναι καλή ιδέα.

Αυτό σχετίζεται με τον προηγούμενο αφορισμό και είναι επίσης αυτονόητο. Εάν η υλοποίηση μπορεί να εξηγηθεί με απλούς όρους, τότε είναι πιθανώς μια καλή ιδέα.

Επειδή ένας τέτοιος κώδικας του οποίου η εφαρμογή μπορεί να περιγραφεί —με απλά λόγια— είναι πολύ πιθανό να είναι ευανάγνωστος και εύκολος να ακολουθηθεί—με ελάχιστη πολυπλοκότητα.

Οι χώροι ονομάτων είναι μια καταπληκτική ιδέα – ας κάνουμε περισσότερα από αυτά!

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

Αυτό μας επιτρέπει να χρησιμοποιούμε αντικείμενα με το ίδιο όνομα —χωρίς διενέξεις— καθώς βρίσκονται σε διαφορετικούς χώρους ονομάτων. Ωστόσο, θα πρέπει να τα χρησιμοποιείτε μόνο όπως απαιτείται και να διασφαλίζετε ότι δεν διακυβεύεται η απλότητα και η αναγνωσιμότητα του κώδικα.

συμπέρασμα

Αυτό είναι όλο για αυτό το σεμινάριο! Ελπίζω ότι αυτός ο οδηγός σας βοήθησε να κατανοήσετε πώς το Zen of Python δίνει έμφαση στο στυλ κώδικα και στις καλές πρακτικές κωδικοποίησης στην Python. Όσο περισσότερο κωδικοποιείτε, τόσο καλύτερα θα το καταφέρετε.

Εάν ενδιαφέρεστε να μάθετε πώς να γράφετε συνοπτικό και ευανάγνωστο κώδικα, διαβάστε αυτό το άρθρο για τα Python one-liners.