Το Python 3 υπάρχει εδώ και επτά χρόνια, ωστόσο μερικοί εξακολουθούν να προτιμούν να χρησιμοποιούν το Python 2 αντί για τη νεότερη έκδοση. Αυτό είναι ένα πρόβλημα ειδικά για τα νεόφυτα που πλησιάζουν την Python για πρώτη φορά. Αυτό το συνειδητοποίησα στον προηγούμενο χώρο εργασίας μου με συναδέλφους στην ίδια ακριβώς κατάσταση. Όχι μόνο δεν γνώριζαν τις διαφορές μεταξύ των δύο εκδόσεων, αλλά δεν γνώριζαν ούτε την έκδοση που είχαν εγκαταστήσει.
Αναπόφευκτα, διαφορετικοί συνάδελφοι είχαν εγκαταστήσει διαφορετικές εκδόσεις του διερμηνέα. Αυτή ήταν μια συνταγή για καταστροφή αν θα προσπαθούσαν τότε να μοιραστούν τυφλά τα σενάρια μεταξύ τους.
Αυτό δεν ήταν δικό τους λάθος, αντίθετα. Απαιτείται μεγαλύτερη προσπάθεια τεκμηρίωσης και ευαισθητοποίησης για να διαλύσει αυτό το πέπλο του FUD (φόβος, αβεβαιότητα και αμφιβολία) που μερικές φορές επηρεάζει τις επιλογές μας. Αυτή η ανάρτηση είναι λοιπόν κατάλληλη για αυτούς ή για όσους χρησιμοποιούν ήδη το Python 2 αλλά δεν είναι σίγουροι για τη μετάβαση στην επόμενη έκδοση, ίσως επειδή δοκίμασαν την έκδοση 3 μόνο στην αρχή όταν ήταν λιγότερο εκλεπτυσμένη και η υποστήριξη για βιβλιοθήκες ήταν χειρότερη.
Πρώτα απ 'όλα, είναι αλήθεια ότι το Python 2 και το Python 3 είναι διαφορετικές γλώσσες; Δεν πρόκειται για μια ασήμαντη ερώτηση. Ακόμα κι αν ορισμένοι θα λύσουν την ερώτηση με: 'Όχι, δεν είναι μια νέα γλώσσα' , στην πραγματικότητα απορρίφθηκαν αρκετές προτάσεις που θα είχαν σπάσει τη συμβατότητα χωρίς να αποφέρουν σημαντικά πλεονεκτήματα .
Το Python 3 είναι μια νέα έκδοση του Python, αλλά δεν είναι απαραίτητα συμβατό προς τα πίσω με κώδικα γραμμένο για το Python 2. Ταυτόχρονα, είναι δυνατό να γράψετε κώδικα που είναι συμβατός και με τις δύο εκδόσεις και αυτό δεν είναι τυχαίο αλλά μια σαφής δέσμευση του Προγραμματιστές Python που συνέταξε τα διάφορα PEP (Python Extension Proposal). Σε λίγες περιπτώσεις στις οποίες η σύνταξη είναι ασύμβατη, χάρη στο γεγονός ότι η Python είναι μια γλώσσα με την οποία μπορούμε να τροποποιήσουμε δυναμικά τον κώδικα κατά το χρόνο εκτέλεσης, μπορούμε να λύσουμε το πρόβλημα χωρίς να βασίζεστε στον προεπεξεργαστή με σύνταξη εντελώς ξένη προς την υπόλοιπη γλώσσα.
Συνεπώς, η σύνταξη δεν αποτελεί πρόβλημα (ειδικά αγνοώντας τις εκδόσεις του Python 3 πριν από το 3.3). Η άλλη μεγάλη διαφορά είναι η συμπεριφορά του κώδικα, η σημασιολογία του και η παρουσία / απουσία μεγάλων βιβλιοθηκών μόνο για μία από τις δύο εκδόσεις. Αυτό είναι πράγματι ένα σημαντικό πρόβλημα, αλλά δεν είναι εντελώς μοναδικό ή νέο για όσους έχουν ήδη εμπειρία με άλλες γλώσσες προγραμματισμού. Πιθανότατα ήδη έχετε πάρει μια παλιά βάση κώδικα / βιβλιοθήκη που δεν μπορεί να δημιουργηθεί με τις πρόσφατες εκδόσεις του ίδιου μεταγλωττιστή που χρησιμοποιήθηκαν αρχικά. Σε αυτήν την περίπτωση, ο ίδιος ο μεταγλωττιστής θα σας βοηθήσει (στην Python, αντ 'αυτού η βοήθεια θα προέλθει από τη δική σας δοκιμαστική σουίτα).
Γιατί να κάνετε τη νέα έκδοση διαφορετική τότε; Ποια πλεονεκτήματα θα φέρουν αυτές οι αλλαγές;
Ας υποθέσουμε ότι θέλουμε να γράψουμε ένα πρόγραμμα για να διαβάσουμε τον κάτοχο των αρχείων / καταλόγων (σε σύστημα Unix) στον τρέχοντα κατάλογό μας και να τα εκτυπώσουμε στην οθόνη.
# encoding: utf-8 from os import listdir, stat # to keep this example simple, we won't use the `pwd` module names = {1000: 'dario', 1001: u'олга'} for node in listdir(b'.'): owner = names[stat(node).st_uid] print(owner + ': ' + node)
Λειτουργούν όλα σωστά; Προφανώς το κάνει. Καθορίσαμε την κωδικοποίηση για το αρχείο που περιέχει τον πηγαίο κώδικα, εάν έχουμε ένα αρχείο που δημιουργήθηκε από τον олга (uid 1001) στον κατάλογό μας το όνομά του θα εκτυπωθεί σωστά και ακόμη και αν έχουμε αρχεία με ονόματα εκτός ASCII αυτά θα εκτυπωθούν σωστά .
Υπάρχει ακόμη μια περίπτωση που δεν έχουμε καλύψει ακόμη: ένα αρχείο που δημιουργήθηκε από την олга AND με χαρακτήρες που δεν είναι ASCII στο όνομα…
su олга -c 'touch é'
Ας προσπαθήσουμε να ξεκινήσουμε ξανά το μικρό μας σενάριο και θα λάβουμε:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
Εάν το σκεφτείτε, μια παρόμοια κατάσταση θα μπορούσε να είναι άσχημη: Έχετε γράψει το πρόγραμμά σας (χιλιάδες γραμμές μήκους αντί για τις λίγες 4 αυτού του παραδείγματος), αρχίζετε να μαζεύετε μερικούς χρήστες, μερικοί από αυτούς ακόμη και από μη αγγλόφωνες χώρες με πιο εξωτικά ονόματα. Όλα είναι εντάξει, έως ότου ένας από αυτούς τους χρήστες αποφασίσει να δημιουργήσει ένα αρχείο που μπορούν να δημιουργήσουν οι χρήστες με μεγαλύτερο όνομα μωσαϊκού χωρίς κανένα πρόβλημα. Τώρα ο κώδικάς σας θα εμφανίσει σφάλμα, ο διακομιστής μπορεί να απαντήσει σε κάθε αίτημα αυτού του χρήστη με σφάλμα 500 και θα πρέπει να σκάψετε στη βάση κώδικα για να κατανοήσετε γιατί ξαφνικά εμφανίζονται αυτά τα σφάλματα.
Πώς μας βοηθάει το Python 3; Εάν προσπαθήσετε να εκτελέσετε το ίδιο σενάριο, θα ανακαλύψετε ότι η Python μπορεί να εντοπίσει αμέσως όταν πρόκειται να εκτελέσετε μια επικίνδυνη λειτουργία. Ακόμα και χωρίς αρχεία με περίεργα ονόματα ή / και δημιουργημένα από ιδιότυπους χρήστες, θα λάβετε αμέσως μια εξαίρεση όπως:
`TypeError: Can't convert 'bytes' object to str implicitly`
Σχετικά με τη γραμμή:
print(owner + ': ' + node)
Κατά τη γνώμη μου, το μήνυμα σφάλματος είναι ακόμη πιο κατανοητό. Το αντικείμενο str είναι owner
, και node
είναι ένα αντικείμενο bytes. Γνωρίζοντας αυτό, είναι προφανές ότι το πρόβλημα οφείλεται στο γεγονός ότι listdir
μας επιστρέφει μια λίστα αντικειμένων byte.
Μια λεπτομέρεια που δεν γνωρίζουν όλοι είναι ότι listdir
επιστρέφει μια λίστα αντικειμένων byte ή συμβολοσειρές unicode ανάλογα με τον τύπο του αντικειμένου που χρησιμοποιήθηκε ως είσοδος. Αποφεύγω τη χρήση listdir('.')
ακριβώς για να αποκτήσετε την ίδια συμπεριφορά στο Python 2 και το Python 3, διαφορετικά στο Python 3 αυτό θα ήταν μια συμβολοσειρά unicode που θα έκανε το σφάλμα να εξαφανιστεί.
Εάν προσπαθήσουμε να αλλάξουμε έναν μόνο χαρακτήρα, από listdir(b'.')
έως listdir(u'.')
θα μπορέσουμε να δούμε πώς λειτουργεί ο κώδικας τόσο στο Python 3 όσο και στο Python 2. Για πληρότητα, πρέπει επίσης να αλλάξουμε 'dario'
έως u'dario'
.
Αυτή η διαφορά στη συμπεριφορά μεταξύ Python 2 και Python 3 υποστηρίζεται ωστόσο από μια ριζική διαφορά στον τρόπο με τον οποίο οι δύο εκδόσεις χειρίζονται τύπους συμβολοσειρών, μια διαφορά που γίνεται αισθητή κατά τη μεταφορά από τη μία έκδοση στην άλλη.
Κατά την άποψή μου, αυτή η κατάσταση είναι εμβληματική του μεγίστου: «οι διαχωριστές μπορούν να εκτοξευθούν ευκολότερα από ό, τι τα κομμάτια μπορούν να χωριστούν». Αυτό που ήταν μαζί στο Python 2 (συμβολοσειρές unicode και προεπιλεγμένες συμβολοσειρές byte, που θα μπορούσαν να εξαναγκαστούν ελεύθερα μαζί) έχει χωριστεί στο Python 3.
Για αυτόν τον λόγο τα εργαλεία όπως 2to3 , ακόμη και αν είναι καλά γραμμένο και εξαιρετικά χρήσιμο για την αυτοματοποίηση της μετατροπής κάθε άλλης διαφοράς, έχουν κάποιους περιορισμούς. Με το bytes / unicode split η διαφορά στις επιφάνειες συμπεριφοράς κατά το χρόνο εκτέλεσης και ένα εργαλείο που μπορεί να κάνει ανάλυση / στατική ανάλυση μόνο δεν θα μπορεί να σας σώσει εάν έχετε μια τεράστια βάση κώδικα Python 2 που συνδυάζει αυτούς τους δύο τύπους. Θα πρέπει να σηκώσετε τα μανίκια σας και να σχεδιάσετε σωστά το API σας για να αποφασίσετε εάν οι λειτουργίες που μέχρι τώρα γίνονται αποδεκτές αδιάκριτα οποιοσδήποτε τύπος συμβολοσειράς θα πρέπει τώρα να λειτουργεί μόνο με μερικές από αυτές (και ποιες). Αντίθετα, αν και γίνεται πολύ λιγότερη χρήση, τα εργαλεία μετατροπής από Python 3 σε Python 2 έχουν πολύ πιο εύκολη ζωή. Ας δούμε ένα παράδειγμα:
Πριν από λίγο, έγραψα ένα διακομιστής HTTP παιχνιδιών (μόνο εξάρτηση: python-magic ), και αυτή είναι η έκδοση για το Python 2 (μετατρέπεται αυτόματα από το Python 3 χωρίς καμία ανάγκη για χειροκίνητες αλλαγές): https://gist.github.com/berdario/8abfd9020894e72b310α
Τώρα, αν θέλετε μπορείτε να ρίξετε μια ματιά απευθείας στο κωδικός μετατράπηκε σε Python 3 με 2to3, ή μπορείτε να το μετατρέψετε απευθείας στο σύστημά σας. Όταν προσπαθείτε να το εκτελέσετε, θα συνειδητοποιήσετε πώς κάθε σφάλμα που μπορείτε να προσπαθήσετε να διορθώσετε με το χέρι σχετίζεται με τη διαίρεση bytes / unicode.
Μπορείτε να εφαρμόσετε μη αυτόματα αλλαγές όπως αυτές: https://gist.github.com/berdario/34370a8bc39895cae139/revisions
Και έτσι, το πρόγραμμα σας λειτουργεί ξανά στο Python 3. Αυτές δεν είναι πολύπλοκες αλλαγές, αλλά απαιτούν ωστόσο να αιτιολογήσουν τους τύπους δεδομένων που λειτουργούν οι λειτουργίες σας και τη ροή ελέγχου. Είναι 13 γραμμές αλλαγών στα 120, μια αναλογία που δεν είναι πολύ εύκολο να χειριστεί: με χιλιάδες γραμμές κώδικα προς θύρα, θα μπορούσατε εύκολα να καταλήξετε με εκατοντάδες για τροποποίηση.
Εάν είστε περίεργοι, μπορείτε να προσπαθήσετε να μετατρέψετε αυτόν τον κωδικό που μόλις φέρατε στο Python 3 πίσω στο Python 2. Χρησιμοποιώντας 3to2 θα λάβετε αυτό: https://gist.github.com/berdario/cbccaf7f36d61840e0ed . Στην οποία η μόνη αλλαγή που έπρεπε να εφαρμοστεί χειροκίνητα είναι .encode('utf-8')
στη γραμμή 55.
Ξεκινώντας από το Python 3 (αν θα χρειαστεί ποτέ να το μετατρέψετε σε Python 2), είναι πολύ πιο εύκολο. Αλλά αν πρέπει να λειτουργήσει ο κώδικάς σας σε άλλη έκδοση, μια ολοκληρωμένη μετατροπή όπως αυτή δεν είναι η καλύτερη επιλογή. Είναι πολύ καλύτερο να διατηρήσετε τη συμβατότητα και με τις δύο εκδόσεις του Πύθων . Για να το κάνετε αυτό μπορείτε να βασιστείτε σε εργαλεία όπως μέλλον .
Ακόμα κι αν δεν έχετε την ευκαιρία να χρησιμοποιήσετε το Python 3 στην παραγωγή (ίσως μία από τις βιβλιοθήκες που χρησιμοποιείτε είναι ογκώδης και συμβατή μόνο με το Python 2), σας προτείνω να διατηρήσετε τον κωδικό σας συμβατό με το Python 3 Θα μπορούσατε ακόμη και στέλεχος / κοροϊδία έξω τις ασύμβατες βιβλιοθήκες, έτσι ώστε να μπορείτε να εκτελείτε συνεχώς τις δοκιμές σας και στις δύο εκδόσεις . Αυτό θα σας διευκολύνει όταν στο μέλλον θα είστε τελικά έτοιμοι να μεταναστεύσετε στο Python 3, για να μην αναφέρουμε πώς μπορεί να σας βοηθήσει να σχεδιάσετε καλύτερα το API σας ή να εντοπίσετε σφάλματα όπως στο παράδειγμα στην αρχή αυτού Θέση.
Όλα αυτά μιλώντας για μεταφορά και διαφορά byte / unicode, ακόμα κι αν αρχικά σκεφτόσασταν να χρησιμοποιήσετε / ξεκινήσετε με το Python 3, ίσως σας οδήγησε να το θεωρήσετε ως το μικρότερο κακό αντί να αντιμετωπίσετε το porting στο μέλλον. Αλλά αν η μεταφορά είναι το ραβδί, πού είναι το καρότο; Προστίθενται τα νέα χαρακτηριστικά στη γλώσσα και στην τυπική βιβλιοθήκη της;
Λοιπόν, μετά από 5 χρόνια από την κυκλοφορία της τελευταίας δευτερεύουσας έκδοσης του Python 2, υπάρχουν πολλά ενδιαφέροντα tidbits που συσσωρεύονται. Για παράδειγμα, βρέθηκα να βασίζομαι αρκετά συχνά σε πράγματα όπως το νέο ορίσματα μόνο για λέξεις-κλειδιά .
Όταν ήθελα να γράψω μια συνάρτηση για να συγχωνεύσω έναν αυθαίρετο αριθμό λεξικών (παρόμοιο με αυτό που κάνει dict.update
, αλλά χωρίς να τροποποιήσω τις εισόδους) θεώρησα φυσικό να προσθέσω ένα όρισμα συνάρτησης για να επιτρέψω στον καλούντα να προσαρμόσει τη λογική. Με αυτόν τον τρόπο θα μπορούσε να γίνει επίκληση αυτής της συνάρτησης ως εξής, για να συγχωνευτούν απλά πολλά λεξικά διατηρώντας τις τιμές στις πιο σωστές επιλογές.
merge_dicts({'a':1, 'c':3}, {'a':4, 'b':2}, {'b': -1}) # {'b': -1, 'a': 4, 'c': 3}
Ομοίως, για συγχώνευση προσθέτοντας τις τιμές:
from operator import add merge_dicts({'a':1, 'c':3}, {'a':4, 'b':2}, {'b': -1}, withf=add) # {'b': 1, 'a': 5, 'c': 3}
Η εφαρμογή ενός τέτοιου API στο Python 2 θα απαιτούσε τον ορισμό ενός **kwargs
εισάγετε και αναζητήστε το withf
διαφωνία. Εάν ο καλούντος πληκτρολόγησε εσφαλμένα το όρισμα ως (π.χ.) withfun
Ωστόσο, το σφάλμα θα αγνοηθεί σιωπηλά. Αντίθετα, στο Python 3 είναι εντάξει να προσθέσετε ένα προαιρετικό όρισμα μετά από μεταβλητά ορίσματα (και θα μπορεί να χρησιμοποιηθεί μόνο με τη λέξη-κλειδί του):
def second(a, b): return b def merge_dicts(*dicts, withf=second): newdict = {} for d in dicts: shared_keys = newdict.keys() & d.keys() newdict.update({k: d[k] for k in d.keys() - newdict.keys()}) newdict.update({k: withf(newdict[k], d[k]) for k in shared_keys}) return newdict
Από το Python 3.5, η αφελής συγχώνευση μπορεί πραγματικά να γίνει με το νέος χειριστής αποσυσκευασίας . Αλλά ακόμη και πριν το 3.5 Python πήρε μια βελτιωμένη μορφή αποσυσκευασίας:
a, b, *rest = [1, 2, 3, 4, 5] rest # [3, 4, 5]
Αυτό είναι διαθέσιμο σε εμάς από το 3.0. Προκειμένου να καταστραφεί, αυτό το είδος αποσυσκευασίας είναι μια περιορισμένη / ad-hoc μορφή του ταιριάσματος μοτίβων που χρησιμοποιείται συνήθως σε λειτουργικές γλώσσες (όπου χρησιμοποιείται επίσης για έλεγχο ροής) και είναι ένα κοινό χαρακτηριστικό σε δυναμικές γλώσσες όπως το Ruby και το Javascript (όπου υποστηρίζεται για το EcmaScript 2015 είναι διαθέσιμο).
Η ____ μειώνει το μέγεθος μιας βάσης δεδομένων και διευκολύνει την εργασία με τα δεδομένα.
Στο Python 2, πολλά API που ασχολήθηκαν με iterables αντιγράφηκαν και τα προεπιλεγμένα είχαν αυστηρή σημασιολογία. Τώρα, αντίθετα, όλα θα δημιουργήσουν τιμές όπως απαιτείται: zip()
, dict.items()
, map()
, range()
. Θέλετε να γράψετε τη δική σας έκδοση enumerate
; Στο Python 3 είναι τόσο απλό όσο η σύνθεση λειτουργιών από την τυπική βιβλιοθήκη μαζί:
zip(itertools.count(1), 'abc')
Είναι ισοδύναμο με enumerate('abc', 1)
.
Δεν θα θέλατε να ορίσετε τα API HTTP τόσο απλά;
@get('/balance') def balance(user_id: int): pass from decimal import Decimal @post('/pay') def pay(user_id: int, amount: Decimal): pass
Όχι πια ''
ad-hoc σύνταξη και τη δυνατότητα χρήσης οποιουδήποτε τύπου / κατασκευαστή (όπως Decimal
) μέσα στις διαδρομές σας χωρίς να χρειάζεται να ορίσετε τον δικό σας μετατροπέα.
Κάτι σαν αυτό έχει ήδη εφαρμοστεί , και αυτό που βλέπετε είναι έγκυρο σύνταξη Python, εκμεταλλευόμενοι τους νέους σχολιασμούς για να είναι πιο βολικό να γράφετε API που είναι επίσης αυτο-τεκμηρίωση.
Αυτά είναι μόνο μερικά απλά παραδείγματα, αλλά οι βελτιώσεις είναι εκτενείς και τελικά σας βοηθούν να γράψετε έναν πιο ισχυρό κώδικα. Ένα παράδειγμα είναι τα tracebacks αλυσίδας εξαίρεσης που ενεργοποιούνται από προεπιλογή, εμφανίζονται στην κατάλληλη ονομασία «Το πιο υποτιμημένο χαρακτηριστικό στο Python 3» από τον Ionel Cristian Mărieș, το οποίο καλύπτεται επίσης από αυτό άλλη ανάρτηση από τον Aaron Maxwell, μαζί με την αυστηρότερη σημασιολογία σύγκρισης του Python 3, και το νέο super
η ΣΥΜΠΕΡΙΦΟΡΑ.
Δεν είναι μόνο αυτό. Υπάρχουν πολλές άλλες βελτιώσεις , αυτά είναι αυτά που πιστεύω ότι έχουν τον μεγαλύτερο αντίκτυπο καθημερινά:
.pyc
αρχείαΈνα πιο λεπτομερές πανόραμα μπορεί να επιτευχθεί με το 'Τι νέα' σελίδες της τεκμηρίωσης, ή για μια άλλη επισκόπηση των αλλαγών, προτείνω και αυτό άλλο Θέση του Aaron Maxwell και αυτών διαφάνειες από το Brett Cannon.
Το Python 2.7 θα υποστηρίζεται έως το 2020 , αλλά μην περιμένετε μέχρι το 2020 για να μεταβείτε σε μια νέα (και καλύτερη) έκδοση!
Σχετίζεται με: Σχέδια σχεδίασης Python: Για κομψό και μοντέρνο κώδικα