Когда допустимо объявлять атрибут экземпляра вне __init__()?

Я начну с описания дилеммы, с которой я сейчас сталкиваюсь. У меня есть класс (назовем его NeatClass). У него есть несколько атрибутов экземпляра, все из которых я, как обычно, объявляю в методе __init__() класса. Я добавляю в класс новый метод (назовем его util_method()), и он требует сохранения внутреннего состояния между вызовами. Я намерен сохранить это состояние в атрибуте экземпляра (назовем его self._util_attr).

Теперь, согласно обычным правилам, я должен объявить self._util_attr внутри NeatClass.__init__():

class NeatClass:
    def __init__():
        self.attr1 = ...
        self.attr2 = ...
        # ...
        self._util_attr = None  # type: ignore

Я понимаю, что объявление атрибутов экземпляра внутри __init__() должно повысить читаемость кода (см. this и это ТАК вопросы) - полностью с этим согласен. Однако в моем случае это может иметь противоположный эффект. Вот почему:

  • В моем классе только util_method() должен использовать self._util_attr.
  • Хотя я хочу, чтобы util_method был частью API моего класса, он будет использоваться только в некоторых редких случаях (большинство экземпляров NeatClass вообще не будут использовать этот метод).
  • Я использую mypy для ввода своего кода. Во время вызова NeatClass.__init__() тип self._util_attr неизвестен (отсюда комментарий # type: ignore). По причинам, выходящим за рамки этого вопроса, я не могу использовать Union или что-то в этом роде для указания типа этой переменной.

Итак, что я действительно хочу сделать, так это:

class NeatClass:
    def __init__():
        self.attr1 = ...
        self.attr2 = ...
        # ...

    def util_method(self):
        if not hasattr(self, "_util_attr"):
            self._util_attr = ...  # initial value of the variable
        # ...

Однако я хочу, чтобы мой код соответствовал как PEP8, так и Руководство по стилю Google Python. Итак, я искал в обоих рекомендациях ссылки на ситуации, в которых допустимо объявление атрибута экземпляра за пределами __init__(). Как вы могли догадаться, я ничего не нашел. На самом деле, я вообще ничего не нашел об объявлении атрибутов экземпляра внутри __init__(), что действительно стало неожиданностью. Это привело нас к моему вопросу:

Есть ли какие-либо ситуации в общих правилах стиля Python (особенно PEP8 и Google), в которых допустимо объявлять атрибуты экземпляра за пределами __init__()__?

Также приветствуются указатели на то, где в этих рекомендациях указано, что атрибуты экземпляра должны быть объявлены только внутри __init__(). Также приветствуются предложения по рефакторингу моего кода.

Обратите внимание, что я не ищу мнения (что, в свою очередь, нарушило бы руководство StackOverflow). Я просто ищу ссылки на разделы наиболее распространенных рекомендаций по стилю Python, которые касаются описанной мной ситуации. Я также не думаю, что этот вопрос является дубликатом - я прочитал несколько других вопросов SO по этому вопросу, и ни один из них, похоже, не задавал то же самое, что и я здесь.

Спасибо за ваше время!


person Talendar    schedule 14.02.2021    source источник
comment
только util_method() должен использовать self.util_attr Тогда util_attr, вероятно, должна быть локальной переменной   -  person DeepSpace    schedule 14.02.2021
comment
Я предполагаю, что util_attr на самом деле должно быть _util_attr, то есть непубличным? Является ли util_attr своего рода кешем?   -  person MisterMiyagi    schedule 14.02.2021
comment
@DeepSpace Спасибо за ответ! Как я указал в вопросе, я должен иметь возможность сохранять внутреннее состояние между вызовами util_method, поэтому оно не может быть локальной переменной. Если бы я использовал C, я думаю, это был бы прецедент для переменной static   -  person Talendar    schedule 14.02.2021
comment
В этом случае вы можете сделать его атрибутом класса, который является версией статических переменных Python. Я не уверен, насколько хорошо mypy играет с атрибутами класса, но вы должны попробовать.   -  person DeepSpace    schedule 14.02.2021
comment
@MisterMiyagi Вы абсолютно правы! Спасибо, что указали на это. я отредактировал вопрос   -  person Talendar    schedule 14.02.2021
comment
@DeepSpace Интересная идея! Я дам ему попробовать. Благодарю вас!   -  person Talendar    schedule 14.02.2021
comment
Вероятно, это не дубликат, потому что вы используете класс, но - Что такое Python-эквивалент статических переменных внутри функции?   -  person Tomerikoo    schedule 14.02.2021
comment
@DeepSpace Использование атрибута класса также не является вариантом, поскольку каждый экземпляр NeatClass должен иметь свой собственный кеш/внутреннее состояние (self._util_attr), возможно, с очень разными значениями. Я думаю, это не было бы вариантом использования статических переменных, как я думал ранее, если бы я программировал на C. Извините, что не понял этого раньше   -  person Talendar    schedule 14.02.2021
comment
@DeepSpace статическая переменная в C на самом деле не похожа на атрибут класса в Python (или на статические атрибуты в других языках OO)   -  person juanpa.arrivillaga    schedule 14.02.2021
comment
В любом случае, что плохого в том, чтобы установить для него какое-то значение по умолчанию и указать его с помощью Any ?   -  person juanpa.arrivillaga    schedule 14.02.2021