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
Блок класса наверху, содержащий общие методы и атрибут класса "kind"; под ним два блока экземпляров, каждый со своим собственным атрибутом "value", со стрелками от каждого экземпляра вверх к классу.

Атрибуты класса живут на самом классе и являются общими. Атрибуты экземпляра живут на каждом экземпляре.

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