Skip to content

Class parameter self - Значение параметра self ​

В методах класса self — это ссылка на текущий экземпляр объекта. Благодаря ему у каждого экземпляра появляется собственное пространство для хранения атрибутов и доступа к методам класса.

Кто такой self?

  • Внутри любого метода экземпляра первым параметром всегда идёт self.
  • По соглашению его называют именно так, но технически это просто имя переменной.

self видимость атрибутов вне класса ​

self определяет видимость объявленных свойств экземпляра класса, делая их доступными через объект.

python
class ClassProperty:
    property_class = "Class property"

    def __init__(self, text: str = "default"):
        self.property_instance_visible = f"Instance property. {text = }"
        property_instance_hidden = f"Hidden local property. {text = }"
        self.total = f"Total: {self.property_instance_visible = }, {property_instance_hidden = }, {text = }"

instance = ClassProperty()
print(f"{instance = }")
print(f"{instance.__dict__ = }")
print(f"{instance.__dir__() = }")
print(f"{instance.property_class = }")
print(f"{instance.property_instance_visible = }")
print(f"{instance.property_instance_hidden = }")    # error
print(f"{instance.total = }")

Пояснение работы кода ​

  • property_class — это свойство класса. Оно хранится в пространстве имён самого класса и доступно всем экземплярам.
  • Внутри __init__ создаются свойства экземпляра self.property_instance_visible и self.total.
  • Локальная переменная property_instance_hidden живёт только во время выполнения конструктора и не сохраняется.
  • instance.__dict__ показывает все свойства, объявленные через self.
  • instance.__dir__() показывает атрибуты экземпляра и методы/свойства класса.
  • Доступ к instance.property_class и instance.property_instance_visible работает без ошибок.
  • При попытке instance.property_instance_hidden возникнет ошибка AttributeError.
  • Итоговое свойство total содержит в себе и видимые, и скрытые значения, но при этом скрытую часть он захвати и сохранил в строке.

Вывод в консоль:

instance = <__main__.ClassProperty object at 0x0000022765937CB0>
instance.__dict__ = {'property_instance_visible': "Instance property. text = 'default'", 'total': 'Total: self.property_instance_visible = "Instance property. text = \'default\'", property_instance_hidden = "Hidden local property. text = \'default\'", text = \'default\''}
instance.__dir__() = ['property_instance_visible', 'total', '__module__', '__firstlineno__', 'property_class', '__init__', '__static_attributes__', '__dict__', '__weakref__', '__doc__', '__new__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__reduce_ex__', '__reduce__', '__getstate__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
instance.property_class = 'Class property'
instance.property_instance_visible = "Instance property. text = 'default'"
instance.total = 'Total: self.property_instance_visible = "Instance property. text = \'default\'", property_instance_hidden = "Hidden local property. text = \'default\'", text = \'default\''

Создание свойств в нескольких методах ​

В Python можно динамически добавлять атрибуты в любом методе, но это может привести к непредсказуемому поведению.

python
class ClassProperty:
    property_class = "Class property"

    def __init__(self):
        property_local_init = "Hidden local property"
        self.property_visible_init = "Visible instance property"

    def any_method(self):
        property_local_any = "Hidden local property"
        self.property_visible_any = "Visible instance property"     # warning

instance = ClassProperty()
print(f"{instance = }")
print(f"{instance.__dict__ = }")
print(f"{instance.__dir__() = }")
print(f"{instance.property_class = }")
print(f"{instance.property_visible_init = }")
# print(f"{instance.property_visible_any = }")        # error
instance.any_method()
print(f"{instance.property_visible_any = }")
# print(f"{instance.property_local_init = }")         # error
# print(f"{instance.property_local_any = }")          # error

Пояснение работы кода с учётом warning и errors ​

  • После instance = ClassProperty() в __init__ создаётся только self.property_visible_init.
  • Словарь instance.__dict__ содержит один ключ: property_visible_init.
  • Пометка # warning указывает на потенциальную проблему: динамическое добавление атрибутов вне конструктора может запутать логику, если методы вызываются не последовательно.
  • Метод any_method не вызывался, поэтому self.property_visible_any не появился, и попытка к нему обратиться также приведёт к ошибке (если метод не был вызван).
  • Вызов метода instance.any_method() - инициализирует свойство self.property_visible_any, поэтому в дальнейшем свойство доступно для работы.
  • Локальные переменные методов скрыты и никак не влияют на состояние экземпляра.

Вывод в консоль:

instance = <__main__.ClassProperty object at 0x0000026854FC7CB0>
instance.__dict__ = {'property_visible_init': 'Visible instance property'}
instance.__dir__() = ['property_visible_init', '__module__', '__firstlineno__', 'property_class', '__init__', 'any_method', '__static_attributes__', '__dict__', '__weakref__', '__doc__', '__new__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__reduce_ex__', '__reduce__', '__getstate__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
instance.property_class = 'Class property'
instance.property_visible_init = 'Visible instance property'
instance.property_visible_any = 'Visible instance property'

self обращение к атрибутам внутри класса ​

С помощью self методы класса могут читать и менять состояние свойств экземпляра внутри класса.

python
class ClassProperty:
    def __init__(self):
        property_local_init = "Hidden local property"
        self.property_visible_init = "Visible instance property"
        self.total = f"{self.property_visible_init} {property_local_init}"

    def any_method(self):
        property_local_any = "Hidden local property"
        self.property_visible_any = "Visible instance property"     # warning
        self.total = f"{self.property_visible_init} {self.property_visible_any} {property_local_any}"

instance = ClassProperty()
print(f"{instance = }")
print(f"{instance.__dict__ = }")
print(f"{instance.__dir__() = }")
print(f"{instance.total = }")
instance.any_method()
print(f"{instance.total = }")

Пояснение работы кода ​

  • После конструктора в instance.__dict__ есть два ключа: property_visible_init и total, где total отражает комбинацию видимого и локального значения.
  • До вызова any_method атрибут property_visible_any отсутствует, поэтому total показывает только свойство из __init__.
  • Внутри any_method создаётся self.property_visible_any и обновляется self.total, объединяя три фрагмента.
  • При каждом вызове метода состояние экземпляра изменяется, и новые значения доступны извне через instance.total.

Вывод в консоль

instance = <__main__.ClassProperty object at 0x000001B37ADE7CB0>
instance.__dict__ = {'property_visible_init': 'Visible instance property', 'total': 'Visible instance property Hidden local property'}
instance.__dir__() = ['property_visible_init', 'total', '__module__', '__firstlineno__', '__init__', 'any_method', '__static_attributes__', '__dict__', '__weakref__', '__doc__', '__new__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__reduce_ex__', '__reduce__', '__getstate__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
instance.total = 'Visible instance property Hidden local property'
instance.total = 'Visible instance property Visible instance property Hidden local property'

self вызов методов внутри класса ​

self не только управляет атрибутами, но и позволяет вызывать методы внутри класса. Вне класса как можно заметить методы вызываются через точку ..

python
class ClassProperty:
    def __init__(self):
        property_local_init = "Hidden local property"
        self.property_visible_init = "Visible instance property"
        self.total = f"{self.property_visible_init} {property_local_init}"
        self.print_method("__init__")

    def do_method(self):
        property_local_any = "Hidden local property"
        self.property_visible_any = "Visible instance property"     # warning
        self.total = f"{self.property_visible_init} {self.property_visible_any} {property_local_any}"
        self.print_method("do_method")

    def print_method(self, text: str):
        print(f"This method call from {text}")
        print(f"{self.total = }")

instance = ClassProperty()
print(f"{instance = }")
print(f"{instance.__dict__ = }")
print(f"{instance.__dir__() = }")
print(f"{instance.total = }")
instance.do_method()
print(f"{instance.total = }")
instance.print_method("print_method")

Пояснение выполнения кода ​

  • В конструкторе после установки атрибутов мгновенно вызывается self.print_method("__init__"), поэтому в консоли появляется первая пара строк.
  • print_method получает доступ к self.total и выводит текущее значение.
  • При вызове instance.do_method() внутри метода вновь обновляется total, затем снова вызывается print_method.
  • Повторный instance.print_method("print_method") демонстрирует, что методы могут вызываться извне с разными параметрами, работая с тем же self.total.
  • Такая схема помогает вызывать вспомогательные методы без повторного дублирования кода.

Вывод в консоль:

This method call from __init__
self.total = 'Visible instance property Hidden local property'
instance = <__main__.ClassProperty object at 0x0000012BBF297E00>
instance.__dict__ = {'property_visible_init': 'Visible instance property', 'total': 'Visible instance property Hidden local property'}
instance.__dir__() = ['property_visible_init', 'total', '__module__', '__firstlineno__', '__init__', 'do_method', 'print_method', '__static_attributes__', '__dict__', '__weakref__', '__doc__', '__new__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__reduce_ex__', '__reduce__', '__getstate__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
instance.total = 'Visible instance property Hidden local property'
This method call from do_method
self.total = 'Visible instance property Visible instance property Hidden local property'
instance.total = 'Visible instance property Visible instance property Hidden local property'
This method call from print_method
self.total = 'Visible instance property Visible instance property Hidden local property'

Локальные переменные vs свойства экземпляра ​

Персистентность — это продолжительность жизни переменной или атрибута, то есть время, в течение которого оно хранится и остаётся доступным:

  • Локальная переменная живёт только внутри одного вызова метода (временная).
  • Атрибут self.x сохраняется в instance.__dict__ до момента удаления экземпляра (долговременная на время жизни объекта).
  • Свойство класса хранится в пространстве имён самого класса и существует ровно до момента выгрузки класса (едино для всех экземпляров).
Тип переменнойЖивёт вДоступ извнеПерсистентность
Локальная (x = ...)Внутри методаНетВременная
Через self.x = ...instance.__dict__Да (instance.x)Долговременная
Свойство классаВнутри классаДа (instance.prop)Одна на класс

self - итог ​

self - можно воспринимать как определение глобальности переменных и функций внутри класса. self можно воспринимать как пространство имён экземпляра. Он делает переменные и методы доступными в рамках одного объекта и отделяет данные разных экземпляров друг от друга.

Дополнительные замечания:

  • Всегда объявляйте основные атрибуты экземпляра в __init__, чтобы не запутывать пользователей класса.
  • Придерживайтесь соглашения именования: не меняйте self на что-то другое, чтобы код оставался понятным.

Упражнения ​

  1. Создайте класс Person с атрибутами name и age. Добавьте метод birthday, который увеличивает age на 1, и сразу же вызывает метод greet, выводящий «Happy 25th birthday, Bob!».

  2. Напишите класс Counter, в котором:

    • В конструкторе храните начальное значение счётчика используя IntVar.
    • Метод increment увеличивает счётчик на заданное число.
    • Метод decrement уменьшает счётчик.
    • Метод reset сбрасывает значение счётчика на ноль.

    Реализуйте оконное представление программы.

    counter.png

  3. Создайте класс проверки строки как числа с методами проверки:

    • целое положительное число,
    • целое отрицательное число,
    • целое число,
    • дробное положительное число,
    • дробное отрицательное число,
    • дробное число,
    • положительное число,
    • отрицательное число,
    • число,
    • число ноль.

    Постарайтесь переиспользовать методы, в решении задания. Проверьте верность работы методов. Используйте данный класс проверки строки как числа для решения калькулятора полей.

  4. Создайте класс Товар - Good с атрибутами: название, цена за единицу, количество, дней на использование товара.

    И методами проверки строки для калькулятора котенка:

    • проверка название товара: не менее двух символов (минимум одной буквы с одно цифрой или буквы),
    • проверка количества товара: целое положительное число, не ноль,
    • проверка цены товара: положительное число, не более двух знаков после запятой,
    • проверка дня: целое положительное число, не ноль,
    • проверка года: целое или половинное число, не ноль.

    Используйте класс в решении программы калькулятора котенка.