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