Home > front end >  How to deal with type checking for optionally None variable, Python3?
How to deal with type checking for optionally None variable, Python3?

Time:09-07

In the example below. The init method of MyClass defined the attribute self._user has optionally the type of UserInput and is initialized as None. The actual user input should be provided by the method set_user. For some practical reason, the user input cannot be provided to the method __init__. After giving user input, other methods method_1 and method_2 can be called.

Question to professional Python programmers: do I really need to add assert ... not None in every method that uses self._user? Otherwise, VS Code Pylance type checking will complain that self._user might be None. However, I tried the same code in PyCharm with its built-in type checking. This issue is not raised there.

And as professional Python programmers, do you prefer the Pylance type checking in VS Code, or the built-in type checking in PyCharm?

Thanks in advance.

class UserInput:
    name: str
    age: int


class MyClass:
    def __init__(self):
        self._user: UserInput | None = None

    def set_user(self, user: UserInput):  # This method should be called before calling any methods.
        self._user = user

    def method_1(self):
        assert self._user is not None  # do I actually need it
        # do something using self._user, for example return its age.
        return self._user.age  # Will get warning without the assert above.

    def method_2(self):
        assert self._user is not None  # do I actually need it
        # do something using self._user, for example return its name.

CodePudding user response:

I think it's safest and cleanest if you keep the asserts in. After all, it is up to the user of your class in which order he calls the instance methods. Therefore, you cannot guarantee that self._user is not None.

CodePudding user response:

I think it's bad practice to use assert in production code. When things go wrong, you get lots of AssertionError, but you don't have any context about why that assertion is being made.

Instead I would catch the issue early, and not handle it later. If set_user() should be called earlier, I'd be tempted to put the user in the __init__ method, but the same principle applies.

@dataclass
class UserInput:
    name: str
    age: int


class NoUserException(TypeError):
    pass


class MyClass:
    # Or this could be in the __init__ method
    def set_user(self, user: UserInput | None):
        if not user:
            raise NoUserException()
        self._user: user


    def method_1(self):
        # do something using self._user, for example return its age.
        return self._user.age

    def method_2(self):
        # do something using self._user, for example return its name.
        return self._user.name

You already stated that set_user will be called first, so when that happens you'll get a NoUserException if the user is None.

But I'd be tempted to not even do that. If I were writing this, I'd have no NoneType checking in MyClass, and instead not call set_user if the user was None.

m = MyClass()
user = ...
if user:
   m.set_user(user)
   ... # anything else with `m`
else:
   # here is where you would get an error
  • Related