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__, що виглядає як виклик, який відтворить об’єкт; налагодження стає значно простішим, коли надруковані значення однозначні.