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

Какие знаешь атрибуты?

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

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

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

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

Атрибуты в Python

Атрибуты (attributes) — это свойства объектов, которые хранят данные. Python имеет гибкую систему атрибутов с особенностями, которых нет в других языках.

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

Связаны с конкретным объектом, не с классом.

class Person:
    def __init__(self, name, age):
        self.name = name  # Экземплярный атрибут
        self.age = age

person1 = Person("Alice", 30)
person2 = Person("Bob", 25)

print(person1.name)  # Alice
print(person2.name)  # Bob

# Разные объекты, разные значения
person1.name = "Alice Johnson"
print(person2.name)  # Bob (не изменился)

# Динамически добавлять атрибуты
person1.email = "alice@example.com"
print(hasattr(person1, 'email'))  # True

2. Атрибуты класса (Class Attributes)

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

class Dog:
    species = "Canis familiaris"  # Атрибут класса
    
    def __init__(self, name):
        self.name = name  # Экземплярный атрибут

dog1 = Dog("Buddy")
dog2 = Dog("Rex")

print(dog1.species)  # Canis familiaris
print(dog2.species)  # Canis familiaris
print(Dog.species)   # Canis familiaris

# Все собаки имеют один species
Dog.species = "Canis lupus"  # Изменяем для всех
print(dog1.species)  # Canis lupus

# Но экземплярный атрибут скрывает класс
dog1.species = "My dog"  # Создали экземплярный атрибут
print(dog1.species)      # My dog (экземплярный)
print(dog2.species)      # Canis lupus (класс)

Важно: экземплярный атрибут скрывает (shadows) атрибут класса!

class Counter:
    count = 0  # Класс атрибут
    
    def __init__(self):
        Counter.count += 1  # Правильно — явно ссылаемся на класс

c1 = Counter()  # count = 1
c2 = Counter()  # count = 2
print(Counter.count)  # 2

3. Приватные атрибуты (Private Attributes)

В Python нет истинной приватности, но есть соглашения:

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Двойной подчеркивание = "приватный"
        self._internal = None     # Одиночный подчеркивание = "внутренний"
    
    def get_balance(self):
        return self.__balance

account = BankAccount(1000)
# account.__balance  # AttributeError: 'BankAccount' object has no attribute '__balance'
# На самом деле Python переименовывает __balance в _BankAccount__balance
print(account._BankAccount__balance)  # 1000 (можешь достучаться, но не надо)

# Это называется "name mangling"
print(dir(account))  # Видны все атрибуты, включая _BankAccount__balance

Лучшая практика: используй одиночный подчеркивание

class GoodClass:
    def __init__(self, value):
        self._value = value  # Сигнал: не трогай извне
    
    @property
    def value(self):
        return self._value
    
    @value.setter
    def value(self, new_value):
        if new_value < 0:
            raise ValueError("Value cannot be negative")
        self._value = new_value

4. Динамические атрибуты через __dict__

Все экземплярные атрибуты хранятся в __dict__:

class MyClass:
    pass

obj = MyClass()
obj.x = 1
obj.y = 2
obj.z = 3

print(obj.__dict__)  # {'x': 1, 'y': 2, 'z': 3}

# Модифицировать __dict__ напрямую
obj.__dict__['w'] = 4
print(obj.w)  # 4

# Удалить атрибут
del obj.x
print('x' in obj.__dict__)  # False

5. Slots — оптимизация памяти

__slots__ ограничивает возможные атрибуты и экономит память:

class OptimizedClass:
    __slots__ = ['x', 'y', 'z']  # Только эти атрибуты разрешены
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.z = None

obj = OptimizedClass(1, 2)
obj.x = 10  # OK
# obj.w = 4  # AttributeError: 'OptimizedClass' object has no attribute 'w'

# Выигрыш памяти: нет __dict__
print(hasattr(obj, '__dict__'))  # False

Когда использовать slots:

  • Миллионы объектов одного класса
  • Когда атрибуты фиксированны
  • Когда критична экономия памяти

6. Свойства (Properties) — @property

Управление доступом к атрибутам:

class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius
    
    @property
    def celsius(self):
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("Below absolute zero!")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32
    
    @fahrenheit.setter
    def fahrenheit(self, value):
        self._celsius = (value - 32) * 5/9

temp = Temperature(25)
print(temp.celsius)      # 25
print(temp.fahrenheit)   # 77.0

temp.fahrenheit = 32  # Переводит в Celsius
print(temp.celsius)   # 0.0

7. Дескрипторы (Descriptors) — продвинутое управление

Полный контроль над get/set/delete операциями:

class Descriptor:
    def __init__(self, name):
        self.name = name
    
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return obj.__dict__.get(self.name, None)
    
    def __set__(self, obj, value):
        if not isinstance(value, (int, float)):
            raise TypeError(f"{self.name} must be numeric")
        obj.__dict__[self.name] = value
    
    def __delete__(self, obj):
        del obj.__dict__[self.name]

class Person:
    age = Descriptor('age')
    height = Descriptor('height')
    
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height

person = Person("Alice", 30, 170)
print(person.age)    # 30
person.age = 31      # OK
# person.age = "thirty"  # TypeError: age must be numeric

8. Магические атрибуты (Dunder Attributes)

Атрибуты, начинающиеся и заканчивающиеся двойным подчеркиванием:

class MyClass:
    def __init__(self):
        pass

obj = MyClass()

print(obj.__class__)           # <class '__main__.MyClass'>
print(obj.__dict__)            # {}
print(MyClass.__name__)        # MyClass
print(MyClass.__module__)      # __main__
print(MyClass.__bases__)       # (object,)
print(MyClass.__doc__)         # None (или docstring)
print(MyClass.__dict__)        # Все методы и атрибуты класса

9. Атрибуты модуля

# mymodule.py
__version__ = "1.0.0"
__author__ = "John Doe"
__all__ = ["MyClass", "my_function"]  # Что экспортировать

class MyClass:
    pass

def my_function():
    pass

# Использование
import mymodule
print(mymodule.__version__)  # 1.0.0
print(mymodule.__author__)   # John Doe

10. getattr(), setattr(), delattr(), hasattr()

Динамическая работа с атрибутами:

class Config:
    debug = True
    timeout = 30

config = Config()

# Получить
value = getattr(config, 'debug')  # True
value = getattr(config, 'missing', 'default')  # default

# Установить
setattr(config, 'host', 'localhost')
print(config.host)  # localhost

# Проверить наличие
if hasattr(config, 'debug'):
    print("Has debug attribute")

# Удалить
delattr(config, 'debug')
print(hasattr(config, 'debug'))  # False

# Получить все атрибуты
print(vars(config))  # {'timeout': 30, 'host': 'localhost'}

Сравнение видимости

Вид атрибута              | Префикс | Видимость          | Практика
--------------------------|---------|-------------------|---------------------
Публичный                 | -       | Везде              | name, age
Внутренний (защищённый)   | _       | Только класс       | _internal, _value
Приватный                 | __      | Скрывается за name | __balance (редко)
Магический                | __...__ | Специальные        | __init__, __str__
Атрибут класса            | -       | На уровне класса   | count, version

Best Practices

  1. Используй @property для контроля доступа:
@property
def value(self):
    return self._value
  1. Одиночный подчеркивание вместо двойного:
self._internal = value  # Лучше, чем __internal
  1. Типизируй атрибуты:
class Person:
    name: str
    age: int
    
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
  1. Используй slots для больших объемов:
class Vector:
    __slots__ = ['x', 'y', 'z']
  1. Не полагайся на приватность:
# В Python это просто соглашение, не обязательство
obj._value = "можешь изменить"