Ответ
В контексте DevOps и автоматизации (например, для создания утилит или модулей управления инфраструктурой) понимание этой разницы критично.
Атрибут класса принадлежит самому классу и является общим для всех его экземпляров. Атрибут экземпляра принадлежит конкретному объекту (экземпляру) и уникален для него.
Пример: Модель сервера в скрипте инвентаризации
class Server:
# Атрибут класса - общий для всех серверов
platform = "Linux"
config_manager = "Ansible"
def __init__(self, hostname, ip_address):
# Атрибуты экземпляра - уникальны для каждого сервера
self.hostname = hostname
self.ip = ip_address
self.services = [] # Инициализируется для каждого экземпляра отдельно
# Создаем экземпляры
web_srv = Server("web01", "192.168.1.10")
db_srv = Server("db01", "192.168.1.20")
# Доступ к атрибуту класса (одинаковый для всех)
print(web_srv.platform) # Вывод: Linux
print(db_srv.config_manager) # Вывод: Ansible
# Доступ к атрибутам экземпляра (разные)
print(web_srv.hostname) # Вывод: web01
print(db_srv.ip) # Вывод: 192.168.1.20
# Изменение атрибута класса влияет на все экземпляры
Server.platform = "Unix"
print(web_srv.platform) # Вывод: Unix (изменилось!)
print(db_srv.platform) # Вывод: Unix (изменилось!)
# Изменение атрибута экземпляра влияет только на него
web_srv.services.append("nginx")
db_srv.services.append("postgresql")
print(web_srv.services) # Вывод: ['nginx']
print(db_srv.services) # Вывод: ['postgresql']
Зачем это важно в DevOps/автоматизации?
- Атрибуты класса отлично подходят для хранения констант, общих настроек или shared state (например, кеша соединений, логгера) для всех объектов этого типа.
- Атрибуты экземпляра используются для хранения уникальных данных объекта (имя хоста, IP, состояние, метрики).
Осторожно: Изменяемые атрибуты класса (например, список default_ports = []) могут привести к неожиданному поведению, если экземпляры будут его изменять. Для изменяемых значений по умолчанию лучше использовать None в атрибуте класса и инициализировать список в __init__.