Кроме атрибутов объектов в классе можно определять атрибуты классов. Подобные атрибуты определяются в виде переменных уровня класса. Например:
class Person:
type = "Person"
description = "Describes a person"
print(Person.type) # Person
print(Person.description) # Describes a person
Person.type = "Class Person"
print(Person.type) # Class Person
Здесь в классе Person определено два атрибута: type, который хранит имя класса, и description, который хранит описание класса.
Для обращения к атрибутам класса мы можем использовать имя класса, например: Person.type, и, как и атрибуты объекта, мы можем получать и изменять их значения.
Подобные атрибуты являются общими для всех объектов класса:
class Person:
type = "Person"
def __init__(self, name):
self.name = name
tom = Person("Tom")
bob = Person("Bob")
print(tom.type) # Person
print(bob.type) # Person
# изменим атрибут класса
Person.type = "Class Person"
print(tom.type) # Class Person
print(bob.type) # Class Person
Атрибуты класса могут применяться для таких ситуаций, когда нам надо определить некоторые общие данные для всех объектов. Например:
class Person:
default_name = "Undefined"
def __init__(self, name):
if name:
self.name = name
else:
self.name = Person.default_name
tom = Person("Tom")
bob = Person("")
print(tom.name) # Tom
print(bob.name) # Undefined
В данном случае атрибут default_name хранит имя по умолчанию. И если в конструктор передана пустая строка для имени, то атрибуту name передается значение атрибута класса default_name. Для обращения к атрибуту класса внутри методов можно применять имя класса
self.name = Person.default_name
Возможна ситуация, когда атрибут класса и атрибут объекта совпадает по имени. Если в коде для атрибута объекта не задано значение, то для него может применяться значение атрибута класса:
class Person:
name = "Undefined"
def print_name(self):
print(self.name)
tom = Person()
bob = Person()
tom.print_name() # Undefined
bob.print_name() # Undefined
bob.name = "Bob"
bob.print_name() # Bob
tom.print_name() # Undefined
Здесь метод print_name использует атрибут объект name, однако нигде в коде этот атрибут не устанавливается. Зато на уровне класса задан атрибут name. Поэтому при первом обращении к методу print_name, в нем будет использоваться значение атрибута класса:
tom = Person() bob = Person() tom.print_name() # Undefined bob.print_name() # Undefined
Однако далее мы можем поменять установить атрибут объекта:
bob.name = "Bob" bob.print_name() # Bob tom.print_name() # Undefined
Причем второй объект - tom продолжит использовать атрибут класса. И если мы изменим атрибут класса, соответственно значение tom.name тоже изменится:
tom = Person() bob = Person() tom.print_name() # Undefined bob.print_name() # Undefined Person.name = "Some Person" # меняем значение атрибута класса bob.name = "Bob" # устанавливаем атрибут объекта bob.print_name() # Bob tom.print_name() # Some Person
Кроме обычных методов класс может определять статические методы. Такие методы предваряются аннотацией @staticmethod и относятся в целом к классу. Статические методы обычно определяют поведение, которое не зависит от конкретного объекта:
class Person:
__type = "Person"
@staticmethod
def print_type():
print(Person.__type)
Person.print_type() # Person - обращение к статическому методу через имя класса
tom = Person()
tom.print_type() # Person - обращение к статическому методу через имя объекта
В данном случае в классе Person определен атрибут класса __type, который хранит значение, общее для всего класса - название класса. Причем поскольку
название атрибута предваряется двумя подчеркиваниями, то данный атрибут будет приватным, что защитит от недопустимого изменения.
Также в классе Person определен статический метод print_type, который выводит на консоль значение атрибута __type. Действие этого метода не зависит от
конкретного объекта и относится в целом ко всему классу - вне зависимости от объекта на консоль будет выводится одно и то же значение атрибута __type. Поэтому такой метод
можно сделать статическим.