2

I will start by describing the dilemma I'm currently facing. I've a class (let's call it NeatClass). It has several instance attributes, all of which I declare, as usual, within the class' __init__() method. I'm adding a new method (let's call it util_method()) to the class and it requires an internal state to be kept between calls. My intention is to store that state in an instance attribute (let's call it self._util_attr).

Now, according to the "usual guidelines", I should declare self._util_attr inside NeatClass.__init__():

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

I understand that declaring instance attributes inside __init__() is supposed to increase the code's readability (see this and this SO questions) - I totally agree with it. However, in my use-case, doing so might have the opposite effect. Here's why:

  • In my class, only util_method() is supposed to use self._util_attr.
  • Although I want util_method to be part of my class' API, it will only be used in some rare occasions (most instances of NeatClass won't use this method at all).
  • I'm using mypy to type-hint my code. During the call to NeatClass.__init__(), the type of self._util_attr isn't known (hence the comment # type: ignore). For reasons that are outside the scope of this question, I can't use Union or something like that to type-hint this variable.

So, what I really want to be doing is this:

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
        # ...

However, I want my code to follow both PEP8 and Google Python Style Guide. So, I searched both guidelines for references to situations in which declaring an instance attribute outside __init__() is acceptable. As you might have imagined, I didn't find anything. As a matter of fact, I didn't find anything about "declaring instance attributes inside __init__()" at all, which really came as a surprise. This lead us to my question:

Are there any situations in Python's common style guidelines (especially PEP8 and Google's) in which it is acceptable to declare instance attributes outside __init__()__?

Pointers to where, in those guidelines, it's specified that instance attributes should only be declared inside __init__() would also be appreciated. Also, suggestions on how to refactor my code are welcome.

Please note that I'm not looking for opinions (which, in turn, would violate StackOverflow's guideline). I'm simply looking for references to sections of Python's most common style guidelines that address the situation I described. I also don't think this question is a duplicate - I've read several other SO's questions on the subject and none of them seemed to be asking the same thing I'm asking here.

Thank you for your time!

Talendar
  • 1,841
  • 14
  • 23
  • 1
    "only `util_method()` is supposed to use `self.util_attr`" Then `util_attr` should probably be a local variable – DeepSpace Feb 14 '21 at 17:09
  • 1
    I assume ``util_attr`` should actually be ``_util_attr``, i.e. non-public? Is ``util_attr`` some sort of cache? – MisterMiyagi Feb 14 '21 at 17:10
  • @DeepSpace Thank you for your response! As I pointed out in the question, I must be able to keep an internal state between calls to `util_method`, so it can't be a local variable. If I were using C, that would be a use-case for a `static` variable, I guess – Talendar Feb 14 '21 at 17:13
  • 3
    In that case, you can make it a class attribute, which is Python's "version" of static variables. I'm not sure how nicely mypy plays with class attributes, but you should give it a go – DeepSpace Feb 14 '21 at 17:15
  • @MisterMiyagi You're absolutely right! Thanks for pointing it out. I've edited the question – Talendar Feb 14 '21 at 17:16
  • @DeepSpace That's an interesting idea! I will give it a try. Thank you! – Talendar Feb 14 '21 at 17:16
  • Probably not a duplicate because you're using a class, but - [What is the Python equivalent of static variables inside a function?](https://stackoverflow.com/questions/279561/what-is-the-python-equivalent-of-static-variables-inside-a-function) – Tomerikoo Feb 14 '21 at 17:35
  • @DeepSpace Using a class attribute isn't an option either, since each instance of `NeatClass` must have its own cache/internal state (`self._util_attr`), possibly with very different values. I guess it wouldn't be a use-case, as I previously thought, for static variables either, if I were programming in C. Sorry about not realizing this before – Talendar Feb 14 '21 at 17:58
  • @DeepSpace a static variable in C isn't really like a class attribute in Python (or like static attributes in other OO languages) – juanpa.arrivillaga Feb 14 '21 at 18:24
  • Anyway, what's wrong with setting it to some default value and type hinting it with `Any` ? – juanpa.arrivillaga Feb 14 '21 at 18:25

0 Answers0