Θα μπορούσε κάποιος να μου εξηγήσει τη σημασία των @classmethod
και @staticmethod
στην python; Πρέπει να γνωρίζω τη διαφορά και τη σημασία τους.
Απ' όσο καταλαβαίνω, η @classmethod
λέει σε μια κλάση ότι πρόκειται για μια μέθοδο που πρέπει να κληρονομηθεί σε υποκλάσεις, ή... κάτι τέτοιο. Ωστόσο, ποιο είναι το νόημα αυτού; Γιατί να μην ορίσετε απλά τη μέθοδο της κλάσης χωρίς να προσθέσετε @classmethod
ή @staticmethod
ή οποιονδήποτε ορισμό @
;
tl;dr: πότε πρέπει να τις χρησιμοποιήσω, γιατί πρέπει να τις χρησιμοποιήσω και πώς πρέπει να τις χρησιμοποιήσω;
Είμαι αρκετά προχωρημένος με τη C++, οπότε η χρήση πιο προχωρημένων εννοιών προγραμματισμού δεν θα πρέπει να αποτελεί πρόβλημα. Μη διστάσετε να μου δώσετε ένα αντίστοιχο παράδειγμα σε C++, αν είναι δυνατόν.
Παρόλο που η classmethod
και η staticmethod
μοιάζουν αρκετά, υπάρχει μια μικρή διαφορά στη χρήση και για τις δύο οντότητες: Η classmethod
πρέπει να έχει μια αναφορά σε ένα αντικείμενο κλάσης ως πρώτη παράμετρο, ενώ η staticmethod
μπορεί να μην έχει καθόλου παραμέτρους.
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
@staticmethod
def is_date_valid(date_as_string):
day, month, year = map(int, date_as_string.split('-'))
return day <= 31 and month <= 12 and year <= 3999
date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')
Ας υποθέσουμε ένα παράδειγμα μιας κλάσης, που ασχολείται με πληροφορίες ημερομηνίας (αυτό θα είναι το boilerplate μας):
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
Αυτή η κλάση προφανώς θα μπορούσε να χρησιμοποιηθεί για την αποθήκευση πληροφοριών σχετικά με συγκεκριμένες ημερομηνίες (χωρίς πληροφορίες για τη ζώνη ώρας- ας υποθέσουμε ότι όλες οι ημερομηνίες παρουσιάζονται σε UTC).
Εδώ έχουμε την __init__
, έναν τυπικό αρχικοποιητή των περιπτώσεων κλάσεων της Python, ο οποίος λαμβάνει ορίσματα ως τυπική μέθοδος instancemethod
, έχοντας το πρώτο μη προαιρετικό όρισμα (self
) που περιέχει μια αναφορά σε μια νεοδημιουργηθείσα περίπτωση.
Μέθοδος κλάσης
Έχουμε κάποιες εργασίες που μπορούν να γίνουν όμορφα με τη χρήση classmethod
.
Ας υποθέσουμε ότι θέλουμε να δημιουργήσουμε πολλές περιπτώσεις της κλάσης Date
που έχουν πληροφορίες ημερομηνίας προερχόμενες από μια εξωτερική πηγή κωδικοποιημένες ως συμβολοσειρά με μορφή 'dd-mm-yyyy'. Ας υποθέσουμε ότι πρέπει να το κάνουμε αυτό σε διαφορετικά σημεία στον πηγαίο κώδικα του έργου μας.
Έτσι, αυτό που πρέπει να κάνουμε εδώ είναι:
Date
περνώντας αυτές τις τιμές στην κλήση αρχικοποίησης.Αυτό θα μοιάζει ως εξής:
day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)
Για το σκοπό αυτό, η C++ μπορεί να υλοποιήσει ένα τέτοιο χαρακτηριστικό με υπερφόρτωση, αλλά η Python δεν διαθέτει αυτή την υπερφόρτωση. Αντ' αυτού, μπορούμε να χρησιμοποιήσουμε την classmethod
. Ας δημιουργήσουμε έναν άλλο "κατασκευαστή".
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
date2 = Date.from_string('11-09-2012')
Ας δούμε πιο προσεκτικά την παραπάνω υλοποίηση, και ας εξετάσουμε ποια πλεονεκτήματα έχουμε εδώ:
cls
είναι ένα αντικείμενο που κατέχει την ίδια την κλάση, όχι μια περίπτωση της κλάσης. Είναι πολύ ωραίο, γιατί αν κληρονομήσουμε την κλάση Date
, όλα τα παιδιά θα έχουν επίσης ορισμένο το from_string
.Στατική μέθοδος
Τι γίνεται με την staticmethod
; Είναι αρκετά παρόμοια με την classmethod
αλλά δεν παίρνει υποχρεωτικές παραμέτρους (όπως κάνει μια μέθοδος κλάσης ή μια μέθοδος παραδείγματος).
Ας δούμε την επόμενη περίπτωση χρήσης.
*Έχουμε μια συμβολοσειρά ημερομηνίας που θέλουμε να επικυρώσουμε με κάποιο τρόπο. Αυτή η εργασία είναι επίσης λογικά συνδεδεμένη με την κλάση Date
που'έχουμε χρησιμοποιήσει μέχρι τώρα, αλλά δεν'απαιτεί την ενσάρκωσή της.
Εδώ είναι που η staticmethod
μπορεί να φανεί χρήσιμη. Ας δούμε το επόμενο κομμάτι κώδικα:
@staticmethod
def is_date_valid(date_as_string):
day, month, year = map(int, date_as_string.split('-'))
return day <= 31 and month <= 12 and year <= 3999
# usage:
is_date = Date.is_date_valid('11-09-2012')
Έτσι, όπως μπορούμε να δούμε από τη χρήση της staticmethod
, δεν έχουμε καμία πρόσβαση στο τι είναι η κλάση--είναι ουσιαστικά απλά μια συνάρτηση, που καλείται συντακτικά όπως μια μέθοδος, αλλά χωρίς πρόσβαση στο αντικείμενο και τα εσωτερικά του (πεδία και άλλες μεθόδους), ενώ η classmethod έχει.
Η απάντηση του Rostyslav Dzinko είναι πολύ κατάλληλη. Σκέφτηκα ότι θα μπορούσα να επισημάνω έναν ακόμη λόγο για τον οποίο θα πρέπει να επιλέγετε την @classmethod
αντί της @staticmethod
όταν δημιουργείτε έναν πρόσθετο κατασκευαστή.
Στο παραπάνω παράδειγμα, ο Rostyslav χρησιμοποίησε την @classmethod
from_string
ως Factory για να δημιουργήσει αντικείμενα Date
από διαφορετικά μη αποδεκτές παραμέτρους. Το ίδιο μπορεί να γίνει και με την @staticmethod
όπως φαίνεται στον παρακάτω κώδικα: