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 с атрибутами: название, цена за единицу, количество, дней на использование товара.

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

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

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