← Назад к вопросам

Какие знаешь особенности статических атрибутов класса?

2.0 Middle🔥 131 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Статические атрибуты класса в Python

Статические атрибуты (class attributes) — это переменные, которые определены на уровне класса и являются общими для всех экземпляров класса. Это отличается от атрибутов экземпляра (instance attributes), которые уникальны для каждого объекта.

Основные особенности

1. Общие для всех экземпляров

Статические атрибуты хранятся в памяти один раз и доступны всем объектам класса:

class BankAccount:
    bank_name = "MyBank"  # Статический атрибут
    interest_rate = 0.05
    
    def __init__(self, owner, balance):
        self.owner = owner  # Атрибут экземпляра
        self.balance = balance

acc1 = BankAccount("Alice", 1000)
acc2 = BankAccount("Bob", 2000)

print(acc1.bank_name)  # "MyBank"
print(acc2.bank_name)  # "MyBank"
print(BankAccount.bank_name)  # "MyBank"

2. Доступ к статическим атрибутам

  • Через класс: ClassName.attribute
  • Через экземпляр: instance.attribute (поиск идёт сначала в экземпляре, потом в классе)
  • Внутри методов класса: self.attribute или ClassName.attribute

3. Модификация статических атрибутов

Это критическая особенность! Если модифицировать через экземпляр, создаётся атрибут экземпляра, не изменяется классовый:

class Counter:
    count = 0

obj1 = Counter()
obj2 = Counter()

print(Counter.count)  # 0
print(obj1.count)     # 0

# Модифицируем через экземпляр
obj1.count = 5

print(obj1.count)     # 5 (атрибут экземпляра)
print(obj2.count)     # 0 (статический атрибут класса)
print(Counter.count)  # 0 (статический атрибут не изменился)

# Правильный способ — модифицировать через класс
Counter.count = 10
print(obj1.count)  # 5 (экземпляр имеет свой атрибут)
print(obj2.count)  # 10 (используется классовый)

Это частая ошибка! Присваивание через self.attr = value создаёт новый атрибут экземпляра, а не изменяет статический.

4. Счётчик и отслеживание экземпляров

Одно из частых применений — счётчик созданных объектов:

class User:
    user_count = 0
    
    def __init__(self, name):
        self.name = name
        User.user_count += 1  # Правильно: модифицируем через класс
    
    @classmethod
    def get_total_users(cls):
        return cls.user_count

user1 = User("Alice")
user2 = User("Bob")
user3 = User("Charlie")

print(User.get_total_users())  # 3
print(User.user_count)         # 3

5. Наследование и переопределение

Дочерние классы наследуют статические атрибуты, но могут их переопределить:

class Animal:
    species = "Unknown"

class Dog(Animal):
    species = "Canis familiaris"

class Cat(Animal):
    species = "Felis catus"

print(Animal.species)  # "Unknown"
print(Dog.species)     # "Canis familiaris"
print(Cat.species)     # "Felis catus"

dog = Dog()
print(dog.species)     # "Canis familiaris"

6. Константы через статические атрибуты

Обычно статические атрибуты используются для хранения констант:

class Math:
    PI = 3.14159
    E = 2.71828
    GRAVITY = 9.8

class Config:
    MAX_RETRIES = 3
    TIMEOUT = 30
    DEBUG = False

7. Декоратор @staticmethod vs @classmethod

Это НЕ статические атрибуты, но часто путаются:

class Example:
    class_var = 10  # Статический атрибут
    
    @staticmethod
    def static_method():
        # Не имеет доступа к self или cls
        # Просто функция, привязанная к классу
        return "Static method"
    
    @classmethod
    def class_method(cls):
        # Имеет доступ к cls
        return f"Class method, class_var = {cls.class_var}"
    
    def instance_method(self):
        # Имеет доступ к self
        return f"Instance method, class_var = {self.class_var}"

Практические примеры

Синглтон через статический атрибут:

class Database:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

db1 = Database()
db2 = Database()
print(db1 is db2)  # True (один и тот же объект)

Реестр объектов:

class Plugin:
    registry = {}  # Статический реестр
    
    def __init__(self, name):
        self.name = name
        Plugin.registry[name] = self
    
    @classmethod
    def get(cls, name):
        return cls.registry.get(name)

p1 = Plugin("auth")
p2 = Plugin("logging")

print(Plugin.get("auth").name)  # "auth"
print(Plugin.registry)  # {"auth": ..., "logging": ...}

Ошибки и подводные камни

  1. Путаница присваивания: self.attr = value создаёт атрибут экземпляра, не изменяет классовый
  2. Мутируемые статические атрибуты: списки и словари общие для всех экземпляров — опасно!
class BadExample:
    items = []  # Опасно!
    
    def add_item(self, item):
        self.items.append(item)  # Изменяет общий список

obj1 = BadExample()
obj2 = BadExample()

obj1.add_item("a")
print(obj2.items)  # ["a"] — ошибка! Список был общим
  1. Правильный способ с мутируемыми объектами:
class GoodExample:
    def __init__(self):
        self.items = []  # Атрибут экземпляра, не статический
    
    def add_item(self, item):
        self.items.append(item)

Знание особенностей статических атрибутов критично для написания корректного Python кода без скрытых ошибок.

Какие знаешь особенности статических атрибутов класса? | PrepBro