Γιατί κολλάς ενώ έχεις κάνει δέκα tutorials
Το περιβάλλον σου
Δεδομένα & τύποι
Λογική & ροή
Συναρτήσεις
Δομές δεδομένων
Errors & debugging
Είσοδος, έξοδος & JSON
Οργάνωση κώδικα & αντικείμενα
Εξαρτήσεις & πακέτα
Τεστ
Project 1: το εργαλείο
Τι σημαίνει backend service
Το πρώτο σου API
Συμβόλαια, validation & σφάλματα
Βάσεις δεδομένων
Auth & ασφάλεια
Cache & background jobs
Ταυτοχρονισμός
Κώδικας που πάει production
Project 2: το service
Απορίες;
Ίδιο περιεχόμενο δεν σημαίνει ίδιο πράγμα
Δύο παραγγελίες έχουν ακριβώς τα ίδια προϊόντα, το ίδιο ποσό και την ίδια διεύθυνση. Είναι η ίδια παραγγελία;
Εξαρτάται τι ρωτάς. Αν ρωτάς αν έχουν το ίδιο περιεχόμενο, ναι. Αν ρωτάς αν είναι η ίδια εγγραφή, δηλαδή αν ακυρώνοντας τη μία ακυρώνεις και την άλλη, τότε πιθανότατα όχι: είναι δύο παραγγελίες, και ο πελάτης πάτησε δύο φορές το κουμπί.
Η Python έχει ξεχωριστό τελεστή για κάθε μία από τις δύο ερωτήσεις. Οι περισσότεροι μαθαίνουν τη διαφορά ως ερώτηση συνέντευξης και την ξεχνούν μέχρι την ημέρα που τους κοστίζει.
Δύο ερωτήσεις, δύο τελεστές
Το
== ρωτάει για το περιεχόμενο. Το is ρωτάει για την ταυτότητα: είναι αυτό ακριβώς το ένα και μοναδικό αντικείμενο;Πρόβλεψε
Διάλεξε μίαa = ["γάλα", "ψωμί"] b = ["γάλα", "ψωμί"] c = a print(a == b, a is b) print(a == c, a is c)Τι τυπώνει, με αυτή τη σειρά;
Στην πράξη το
== είναι αυτό που θέλεις σχεδόν πάντα. Όταν συγκρίνεις ένα ΑΦΜ, μια τιμή, ένα όνομα πελάτη, σε ενδιαφέρει το περιεχόμενο. Δεν σε ενδιαφέρει αν το string ήρθε από το αρχείο ή από τη βάση.Το
is το θέλεις σε μία περίπτωση που θα τη συναντάς κάθε μέρα: x is None. Το None υπάρχει μία μόνο φορά σε ολόκληρο το πρόγραμμα, οπότε η ερώτηση «είναι εκείνο ακριβώς;» είναι η σωστή ερώτηση.Η παγίδα, και γιατί δεν είναι απλώς παγίδα
Εδώ οι περισσότεροι οδηγοί σου δείχνουν το εξής και προχωράνε. Βάλε τις τρεις γραμμές σε ένα αρχείο
ints.py και τρέξε το:pythona = 257 b = 257 print(a is b)
Τυπώνει
True.Τώρα άνοιξε το REPL και γράψε τις ίδιες γραμμές, μία μία:
text>>> a = 257 >>> b = 257 >>> a is b False
Ίδιος κώδικας, δύο απαντήσεις.
Η εξήγηση είναι απλή. Όταν ο interpreter μεταφράζει ένα ολόκληρο αρχείο, βλέπει και τα δύο 257 μαζί και δεν μπαίνει στον κόπο να φτιάξει δύο ίδιους αριθμούς: φτιάχνει έναν και τον χρησιμοποιεί δύο φορές. Στο REPL κάθε γραμμή μεταφράζεται ξεχωριστά, οπότε τη δεύτερη φορά δεν ξέρει τίποτα για την πρώτη και φτιάχνει καινούριο αντικείμενο.
Υπάρχει και δεύτερη έκπληξη. Άλλαξε το 257 σε 256 και το REPL απαντάει
True. Η υλοποίηση της Python φτιάχνει από την αρχή τους ακέραιους από το -5 μέχρι το 256, επειδή εμφανίζονται συνέχεια, και τους μοιράζει σε όποιον τους ζητήσει.Και τώρα το σημαντικό: τίποτα από τα παραπάνω δεν είναι κανόνας της γλώσσας. Είναι αποφάσεις της συγκεκριμένης υλοποίησης, μπορούν να αλλάξουν σε επόμενη έκδοση και είναι ήδη διαφορετικές ανάλογα με το πού τρέχεις τον κώδικα.
Οπότε δεν σου τα λέω για να τα θυμάσαι. Σου τα λέω για να μη χρησιμοποιήσεις ποτέ
is σε αριθμό ή σε string. Ένας έλεγχος που δίνει άλλη απάντηση στο REPL όπου τον δοκίμασες και άλλη στο αρχείο όπου τον έβαλες δεν είναι παγίδα ερώτησης, είναι bug που δεν θα το αναπαράγεις.Η ίδια η Python σε προειδοποιεί, αν την ακούσεις:
textSyntaxWarning: "is" with a literal. Did you mean "=="?
Το βλέπεις πριν τρέξει καν το πρόγραμμα, γιατί εντοπίζεται στη μετάφραση. Και είναι προειδοποίηση, όχι σφάλμα: το πρόγραμμα συνεχίζει κανονικά και τυπώνει το λάθος αποτέλεσμα.
Το == δεν σημαίνει «ίδια bits»
Μέχρι εδώ θα μπορούσες να συμπεράνεις ότι το
== είναι το χαλαρό και το is το αυστηρό. Δεν είναι τόσο απλό, και εδώ αρχίζει το μέρος που δεν το καλύπτει καμία ερώτηση συνέντευξης.Το
== δεν έχει μία σημασία. Κάθε τύπος ορίζει τι σημαίνει «ίσο» για αυτόν. Οι λίστες συγκρίνουν στοιχείο προς στοιχείο. Τα strings χαρακτήρα προς χαρακτήρα. Οι αριθμοί συγκρίνουν αριθμητική αξία, ακόμα και όταν είναι διαφορετικού τύπου:pythonwhole = 1 decimal_one = 1.0 print(whole == decimal_one) print(whole is decimal_one)
True και μετά False. Ένας ακέραιος και ένας δεκαδικός είναι δύο διαφορετικά αντικείμενα, με διαφορετικό τύπο και διαφορετική αναπαράσταση στη μνήμη, και παρ' όλα αυτά είναι ίσοι. Γιατί η ερώτηση «είναι ίδιος αριθμός;» έχει προφανή απάντηση και η γλώσσα τη δίνει.Το ίδιο ισχύει και για το
True, που στην Python είναι αριθμός: το 1 == True δίνει True.Και από την ανάποδη, δύο τιμές του ίδιου ακριβώς τύπου μπορεί να μην είναι ίσες ενώ εσύ νομίζεις ότι είναι, όπως το
0.1 + 0.2 με το 0.3.Η τιμή που δεν είναι ίση με τον εαυτό της
Το τελευταίο κομμάτι είναι εκείνο που δείχνει καθαρότερα από κάθε άλλο ότι οι δύο ερωτήσεις είναι όντως ανεξάρτητες.
Υπάρχει μια ειδική δεκαδική τιμή, το «δεν είναι αριθμός», που προκύπτει από πράξεις χωρίς αριθμητικό αποτέλεσμα και εμφανίζεται συχνά σε δεδομένα που ήρθαν από αλλού:
pythonmissing = float("nan") print(missing == missing) print(missing is missing)
textFalse True
Η ίδια τιμή, η ίδια μεταβλητή, δύο γραμμές μία κάτω από την άλλη. Δεν είναι ίση με τον εαυτό της, και ταυτόχρονα είναι αναμφισβήτητα το ίδιο αντικείμενο.
Δεν είναι bug, το επιβάλλει το πρότυπο των δεκαδικών: αν δύο μετρήσεις είναι και οι δύο άγνωστες, δεν έχεις κανέναν λόγο να πεις ότι είναι ίδιες.
Και μια συνέπεια που την πληρώνει όποιος κάνει επεξεργασία δεδομένων:
pythonmissing = float("nan") print(missing in [missing])
Τυπώνει
True. Ο έλεγχος «υπάρχει μέσα στη λίστα;» ρωτάει πρώτα «είναι το ίδιο αντικείμενο;» και μόνο αν η απάντηση είναι όχι προχωράει στο ==. Είναι μια βελτιστοποίηση με λογική: αν είναι κυριολεκτικά το ίδιο πράγμα, φυσικά και βρίσκεται μέσα.Οπότε αυτή η τιμή δεν βρίσκεται με σύγκριση, βρίσκεται όμως με αναζήτηση σε λίστα. Αν κρατάς μόνο το μοντέλο «το
in χρησιμοποιεί το ==», αυτό το αποτέλεσμα δεν εξηγείται.Οι τέσσερις κανόνες που κρατάς
Το
== για όλα όσα συγκρίνεις σε κανονικό κώδικα. Τιμές, ονόματα, κωδικούς, καταστάσεις.Το
is μόνο για το None, σε αυτή τη μορφή: if value is None και if value is not None.Ποτέ
is δίπλα σε αριθμό ή string γραμμένο κυριολεκτικά. Αν η Python σου βγάλει το SyntaxWarning, μην ψάξεις τρόπο να το κρύψεις. Άλλαξε τον τελεστή.Και για booleans, ούτε το ένα ούτε το άλλο. Δεν γράφεις
if is_paid == True και ούτε if is_paid is True. Γράφεις if is_paid, γιατί έχει ήδη την απάντηση μέσα του.Σύνοψη
- Το
==ρωτάει για το περιεχόμενο, τοisγια την ταυτότητα. Δύο ανεξάρτητες ερωτήσεις, και η μία δεν συνεπάγεται την άλλη. - Δύο λίστες με ίδια στοιχεία είναι ίσες και δεν είναι το ίδιο αντικείμενο. Δύο ονόματα πάνω στην ίδια λίστα είναι και τα δύο μαζί: ίσα και το ίδιο αντικείμενο.
- Το
a is bγια δύο ίσους αριθμούς δίνει άλλη απάντηση σε αρχείο και άλλη στο REPL. Δεν το ορίζει η γλώσσα, το αποφασίζει η υλοποίηση, οπότε δεν χτίζεις τίποτα πάνω του. - Το
SyntaxWarning: "is" with a literalεμφανίζεται πριν τρέξει το πρόγραμμα και δεν το σταματάει. Είναι από τις πιο χρήσιμες προειδοποιήσεις της γλώσσας. - Το
==το ορίζει ο κάθε τύπος. Ένας ακέραιος και ένας δεκαδικός με ίδια αξία είναι ίσοι χωρίς να είναι το ίδιο πράγμα. - Το
float("nan")δεν είναι ίσο με τον εαυτό του, αλλά βρίσκεται μέσα σε λίστα που το περιέχει, γιατί ο έλεγχος περιεκτικότητας κοιτάει πρώτα ταυτότητα. - Στην πράξη:
==παντού,isμόνο για τοNone, τίποτα από τα δύο για booleans.
Θες να συνεχίσεις;
Αυτό το μάθημα είναι δωρεάν. Κάνε σύνδεση για να κρατάς την πρόοδο και τις σημειώσεις σου, ή απόκτησε το course για να ξεκλειδώσεις τα υπόλοιπα μαθήματα, τα labs και την κοινότητα.