Кроме атрибутов объектов в классе можно определять атрибуты классов. Подобные атрибуты определяются в виде переменных уровня класса. Например:
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. Поэтому такой метод
можно сделать статическим.