Jinghao Shi
Jinghao Shi

Reputation: 1087

Python Typing Annotation inside __init__

How to annotate the type of an instance variable that is only avaiable after __init__? I'd like to list all instance attributes inside __init__ as per POLS.

MWE:

class MyClass(object):
   def __init__(self):
      self.foo :Union[CustomClass, None] = None

   def set_foo(self):
      self.foo = CustomClass()

   def use_foo(self):
      self.foo.do_something()

Inside __init__, if I just annotate foo as self.foo: CustomClass = None, Pylint will complain:

T484: Incompatible types in assignment (expression has type None, variable has type "CustomClass").

However, if I annotate foo as self.foo: Union[CustomClass, None] = None (as in above MWE), PyLint will then complain inside use_foo function:

T484: "None" has no attribute "do_something".

How do I make PyLint happy? (w/o disabling T484)

Upvotes: 2

Views: 974

Answers (1)

Shadow
Shadow

Reputation: 9427

The easiest way I can think of is to simply initialise self.foo to "" instead of None.

This means that self.foo.upper() will be available, so pylint will have no cause to complain.

If you don't want use_foo to be useable until get_foo (which probably would be better called set_foo) is called, you could check to make sure that self.foo is populated, or keep a boolean field stating whether it has ever been run.


If your class is a little more complicated than a string, you'll have to do a quick check before using self.foo.

def use_foo(self):
    if self.foo is None:
        raise EnvironmentError("You haven't called get_foo!")
    self.foo.upper()

This is not a very clean solution - I think we can do better.

Lets try outsourcing this check to a decorator;

def ensure_foo(func):
    def inner_func(self, *args, **kwargs):
        if self.foo is None:
            raise EnvironmentError("You haven't called get_foo!")
        func(self, *args, **kwargs)
    return inner_func

I haven't personally tried this with pylint - but if pylint is clever enough to work out what's going on, slapping @ensure_foo on top of your class methods is going to be a lot cleaner than putting None checks everywhere...

Upvotes: 2

Related Questions