2.21. מתודות ותכונות

מתודה היא פונקציה המוגדרת בתוך מחלקה. הפרמטר הראשון הוא המופע שעליו נקראת המתודה; המוסכמה היא לקרוא לו self.

2.21.1. מתודות מופע

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def magnitude(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

p = Point(3, 4)
print(p.magnitude())

פלט:

5.0

הקריאה p.magnitude() מעבירה את p כ-self אוטומטית. גופי מתודות קוראים ומעדכנים תכונות דרך self באותה דרך שפונקציות קוראות ומעדכנות שמות דרך פרמטרים.

2.21.2. תכונות מופע מול תכונות מחלקה

תכונות המשויכות ל-self – בדרך כלל בתוך __init__ – שייכות לאותו מופע יחיד:

a = Point(1, 2)
b = Point(10, 20)
a.x = 99
print(a.x, b.x)           # 99 10

a.x ו-b.x הם אחסון שונה; שינוי של אחד אינו משפיע על האחר.

שמות המשויכים בגוף המחלקה, מחוץ לכל מתודה, משותפים לכל מופע של המחלקה:

class Counter:
    kind = "tally"        # class attribute -- shared

    def __init__(self):
        self.value = 0    # instance attribute -- per instance

a = Counter()
b = Counter()
print(a.kind, b.kind)     # tally tally
A class block at the top holding shared methods and the class attribute "kind"; below it, two instance boxes each holding their own "value" attribute, with arrows from each instance up to the class.

תכונות מחלקה שוכנות על המחלקה עצמה ומשותפות. תכונות מופע שוכנות על כל מופע.

מגיעים לתכונת מחלקה באותה דרך כמו לתכונת מופע (a.kind); Python מחפשת קודם על המופע, אחר כך על המחלקה. השמה ל-a.kind תיצור תכונת מופע חדשה המסתירה את זו של המחלקה, ותשאיר את b.kind ללא שינוי.

השתמשו בתכונות מחלקה עבור קבועים וברירות מחדל שכל מופע אמור לראות באותה צורה. השתמשו בתכונות מופע עבור מצב שכל אובייקט אמור להחזיק עותק משלו.

2.21.3. __str__ ו-__repr__

שתי מתודות מיוחדות שולטות באופן שבו מופע מודפס. __str__ מחזירה את המחרוזת ה“ידידותית“ המשמשת את print(); __repr__ מחזירה את מחרוזת ה“מפתח“ המשמשת ב-REPL ובתוך תצוגות של מכלים:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return "(" + str(self.x) + ", " + str(self.y) + ")"

    def __repr__(self):
        return "Point(" + str(self.x) + ", " + str(self.y) + ")"

p = Point(3, 4)
print(p)            # uses __str__
print([p, p])       # uses __repr__ inside the list

פלט:

(3, 4)
[Point(3, 4), Point(3, 4)]

אם רק __repr__ מוגדרת, print() נופלת חזרה אליה – כך ש-__repr__ יחידה כתובה היטב בדרך כלל מספיקה. הגדירו גם __str__ רק כאשר הצורה הידידותית אמורה להיראות שונה מצורת המפתח. שאפו ל-__repr__ שנראה כמו הקריאה שתיצור מחדש את האובייקט; ניפוי באגים נעשה דרמטית קל יותר כאשר ערכים מודפסים חד-משמעיים.