Атрибуты хранят состояние объекта, а методы определяют его поведение.

3.1. Атрибуты Экземпляра (Instance Attributes)

  • Принадлежат конкретному объекту (экземпляру класса).
  • Значения этих атрибутов могут быть уникальными для каждого объекта.
  • Обычно определяются внутри метода __init__ с использованием self.attribute_name = value.
  • Также могут быть добавлены или изменены для конкретного объекта в любой момент после его создания.
class Car:
    def __init__(self, make, model, year):
        # Атрибуты экземпляра
        self.make = make
        self.model = model
        self.year = year
        self.mileage = 0 # Начальное значение для всех машин
 
car1 = Car("Toyota", "Camry", 2021)
car2 = Car("Ford", "Mustang", 2022)
 
print(f"{car1.make} {car1.model} ({car1.year})") # Toyota Camry (2021)
print(f"{car2.make} {car2.model} ({car2.year})") # Ford Mustang (2022)
 
car1.mileage = 15000 # Изменяем атрибут для конкретного объекта
print(f"Пробег car1: {car1.mileage}") # 15000
print(f"Пробег car2: {car2.mileage}") # 0
 
car1.color = "Синий" # Динамически добавляем атрибут
print(f"Цвет car1: {car1.color}") # Синий
# print(car2.color) # AttributeError: 'Car' object has no attribute 'color'

3.2. Атрибуты Класса (Class Attributes)

  • Принадлежат самому классу, а не конкретному экземпляру.
  • Являются общими для всех экземпляров этого класса.
  • Определяются непосредственно внутри класса, но вне каких-либо методов.
  • Доступны как через имя класса (ClassName.attribute), так и через экземпляр (instance.attribute), если не переопределены на уровне экземпляра.
class Dog:
    # Атрибут класса - общий для всех собак
    species = "Canis familiaris"
    num_legs = 4
 
    def __init__(self, name):
        self.name = name # Атрибут экземпляра
 
dog1 = Dog("Бобик")
dog2 = Dog("Шарик")
 
# Доступ к атрибуту класса
print(f"{dog1.name} - вид: {dog1.species}") # Бобик - вид: Canis familiaris
print(f"{dog2.name} - вид: {dog2.species}") # Шарик - вид: Canis familiaris
print(f"Все собаки - вид: {Dog.species}")   # Все собаки - вид: Canis familiaris
 
# Изменение атрибута класса затрагивает все экземпляры (если они не переопределили его)
# Dog.species = "Canis lupus familiaris"
# print(f"Новый вид dog1: {dog1.species}") # Новый вид dog1: Canis lupus familiaris
 
# Переопределение атрибута на уровне экземпляра
dog1.species = "Домашний любимец"
print(f"Вид dog1 (переопределен): {dog1.species}") # Вид dog1 (переопределен): Домашний любимец
print(f"Вид dog2 (не изменился): {dog2.species}")  # Вид dog2 (не изменился): Canis familiaris
print(f"Вид класса (не изменился): {Dog.species}") # Вид класса (не изменился): Canis familiaris

3.3. Методы Экземпляра (Instance Methods)

  • Самый распространенный тип методов.
  • Принимают self в качестве первого аргумента.
  • Могут получать доступ и изменять атрибуты конкретного экземпляра (self.attribute), а также атрибуты класса (self.species или Dog.species).
  • Вызываются для объекта: my_object.method_name().
class Circle:
    pi = 3.14159 # Атрибут класса
 
    def __init__(self, radius):
        self.radius = radius # Атрибут экземпляра
 
    # Метод экземпляра
    def calculate_area(self):
        return self.pi * (self.radius ** 2)
 
    # Другой метод экземпляра
    def describe(self):
        print(f"Круг с радиусом {self.radius}")
 
c1 = Circle(5)
c2 = Circle(10)
 
c1.describe() # Круг с радиусом 5
print(f"Площадь c1: {c1.calculate_area()}") # Площадь c1: 78.53975
 
c2.describe() # Круг с радиусом 10
print(f"Площадь c2: {c2.calculate_area()}") # Площадь c2: 314.159

3.4. Методы Класса (Class Methods)

  • Определяются с помощью декоратора @classmethod.
  • Принимают класс (cls) в качестве первого аргумента (вместо self).
  • Могут получать доступ и изменять атрибуты класса, но не атрибуты конкретного экземпляра (так как у них нет self).
  • Часто используются как альтернативные конструкторы или для работы с состоянием класса.
  • Вызываются как для класса (ClassName.method_name()), так и для объекта (my_object.method_name()).
class Employee:
    raise_amount = 1.04 # Атрибут класса
 
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay
 
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount) # Использует атрибут класса
 
    @classmethod
    def set_raise_amount(cls, amount): # cls ссылается на класс Employee
        print(f"Изменяем ставку повышения для класса {cls.__name__}")
        cls.raise_amount = amount
 
    # Альтернативный конструктор через метод класса
    @classmethod
    def from_string(cls, emp_str):
        first, last, pay = emp_str.split('-')
        # Вызывает __init__ класса cls (Employee)
        return cls(first, last, int(pay))
 
emp1 = Employee("Иван", "Иванов", 50000)
emp2 = Employee("Петр", "Петров", 60000)
 
print(f"Ставка повышения: {Employee.raise_amount}") # 1.04
Employee.set_raise_amount(1.05) # Вызов метода класса через класс
print(f"Новая ставка повышения: {Employee.raise_amount}") # 1.05
print(f"Новая ставка у emp1: {emp1.raise_amount}")     # 1.05 (доступ через экземпляр)
 
emp_str_3 = "Мария-Сидорова-70000"
emp3 = Employee.from_string(emp_str_3) # Используем альтернативный конструктор
print(f"Создан сотрудник: {emp3.first} {emp3.last} с ЗП {emp3.pay}")

3.5. Статические Методы (Static Methods)

  • Определяются с помощью декоратора @staticmethod.
  • Не принимают ни self, ни cls в качестве первого аргумента.
  • Ведут себя как обычные функции, но помещены внутрь класса для логической организации.
  • Не могут напрямую изменять состояние объекта или класса (у них нет доступа к self или cls).
  • Используются для создания утилитных функций, связанных с классом, но не зависящих от его состояния.
  • Вызываются как для класса (ClassName.method_name()), так и для объекта (my_object.method_name()).
import datetime
 
class MathUtils:
    @staticmethod
    def add(x, y):
        return x + y
 
    @staticmethod
    def is_workday(day):
        # weekday() возвращает 0 для понедельника, ..., 6 для воскресенья
        if day.weekday() == 5 or day.weekday() == 6:
            return False # Суббота или Воскресенье
        return True
 
print(MathUtils.add(5, 3)) # 8
 
today = datetime.date.today()
if MathUtils.is_workday(today):
    print("Сегодня рабочий день.")
else:
    print("Сегодня выходной.")

〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰