pythonmypypython-typingpython-3.11

Python 3.11 declare variable typing without assigning value


I've been wondering what's the most pythonic way and also most readable way to declare a variable's type without directly assigning a value in Python 3.11

Given a class

class MyClass:
    def __init__(self, value: str):
        self.value: str = value

I could declare the variable without assigning a value:

def test():
    variable: MyClass
    if True:
        variable = MyClass(value="value")
    else:
        variable = MyClass(value="otherValue")

That would work but the variable is non existant when trying to use it so it's there just for readability.

I could use Optional

def test():
    variable: Optional[MyClass] = None
    if True:
        variable = MyClass(value="value")
    else:
        variable = MyClass(value="otherValue")
    assert isinstance(variable, MyClass)

Or I could type it on first use

def test():
    if True:
        variable: MyClass = MyClass(value="value")
    else:
        variable = MyClass(value="otherValue")

I couldn't find anything in Peps, chatGpt4 is contradicting itself, and google sends me to 2015 GitHub's questions. The tool MyPy seems to be fine with the three of them, but I wonder, is there a "correct" way ?

Thank you!


Solution

  • Ok, by parsing Pep again here's what I found:

    In stubs it may be useful to declare the existence of a variable without giving it an initial value. This can be done using PEP 526 variable annotation syntax:

    from typing import IO
    
    stream: IO[str]
    

    The above syntax is acceptable in stubs for all versions of Python. However, in non-stub code for versions of Python 3.5 and earlier there is a special case:

    from typing import IO
    
    stream = None  # type: IO[str]
    

    Type checkers should not complain about this (despite the value None not matching the given type), nor should they change the inferred type to Optional[...] (despite the rule that does this for annotated arguments with a default value of None). The assumption here is that other code will ensure that the variable is given a value of the proper type, and all uses can assume that the variable has the given type.

    So the "correct" way seems to be

    def test():
        variable = None  # type: MyClass
        if True:
            variable = MyClass(value="value")
        else:
            variable = MyClass(value="otherValue")
    

    So it seems we're still using comments as a way of typing which I'm personally not a big fan of.

    Edit: As noted by InSync in the comment, I did not read correctly, the comment is needed for python 3.5 and earlier. So this version seems to be the one recommended

    from typing import IO
    
    stream: IO[str]