Is it a good practice to declare instance variables as None in a class in Python?

  • Consider the following class:

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

    My coworkers tend to define it like this:

    class Person:
        name = None
        age = None
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    

    The main reason for this is that their editor of choice shows the properties for autocompletion.

    Personally, I dislike the latter one, because it makes no sense that a class has those properties set to None.

    Which one would be better practice and for what reasons?

    Never let your IDE dictate what code you write?

    By the way: using a proper python IDE (e.g. PyCharm), setting the attributes in the `__init__` already provides autocompletion etc. Also, using `None` prevents the IDE to infer a better type for the attribute, so it's better to use a sensible default instead (when possible).

    If it is just for autocompletion you can use type hinting, and the additional docstrings will be a plus too.

    If you want to predefine the attributes your instance will use, add `__slots__` to your class.

    "Never let your IDE dictate what code you write" is a debated issue. As of Python 3.6 there's in-line annotations and a `typing` module, which allow you to provide hints to the IDE and linter, if that sort of thing tickles your fancy...

    These assignments at class level have no effect on the rest of the code. They have no effect on `self`. Even if `self.name` or `self.age` were not assigned in `__init__` they would not show up in the instance `self`, they only show up in the class `Person`.

    Actually self.name gets copied from the class variable, even if not explicitly declared, which is a little confusing. The class variable gets updated by calling . versus self.. Example: `class test(): x=1 y=7 def __init__(self,x): self.x=x self.y=x*2 def instx(self): return self.x @classmethod def classx(cls): return test.x def gety(self): print (self.y) print (test.y) return self.y a=test(2) print(a.instx()) print(a.classx()) a.gety() ` Output: `2 1 4 7 `

  • StarWeaver

    StarWeaver Correct answer

    6 years ago

    I call the latter bad practice under the "this does not do what you think it does" rule.

    Your coworker's position can be rewritten as: "I am going to create a bunch of class-static quasi-global variables which are never accessed, but which do take up space in the various class's namespace tables (__dict__), just to make my IDE do something."

    A bit like docstrings then ;-) Of course, Python has a handy mode to strip those, `-OO`, for those few who need it.

    The space taken is by far the least important reason this convention stinks. It's a dozen bytes per name (per process). I feel like having wasted my time just *reading* the part of your answer pertaining to it, that's how unimportant it the space cost.

    @delnan I agree about the memory size of the entries being meaningless, i was more thinkin gof the logical/mental space taken up, making introspective debugging and such require more reading and sorting, i would guess. I~LL

    Actually, if you were this paranoid about space, you would notice that this saves space, and wastes time. The uninitialized members fall through to use the class value, and therefore there aren't a bunch of copies of the variable name mapped to None (one for each instance). So the cost is a few bytes at the class and the savings is a few bytes per instance. But each failing variable name search costs a tiny bit of time.

    If you were worried about 8 bytes you wouldn't be using Python.

    So if the code at class level is removed, is it a good practice to initialize instance variables to `None` in `__init__`? PyCharm always complaint when a variable is first seen in another function.

    @JonJayObermark *The uninitialized members fall through to use the class value (…)* This is not the case here as both attributes are initialised in `__init__()`.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM