Invalid type after function with side-effect on instance variable? #8157
-
Apologies for my ignorance, I'm not sure if this is due to some fundamentally 'wrong' pattern I'm doing here, but can anyone explain this behavior to me, and maybe suggest a way to get "correct" typing for a pattern like this? class Test:
def __init__(self) -> None:
self.x = None
def set_x(self, value: str):
self.x = value
t = Test()
if t.x is None:
t.set_x("hello")
t.x # interprets as 'None' when it is 'str' in reality
t.x # interprets as 'str | None' when it is 'str' in reality |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Static type checkers narrow the type of expressions locally within an execution context (e.g. within a function body or within the module's global scope). They do not track side effects by other execution contexts. In your example, the In general, it's best to avoid writing methods that have side effects like this, especially when they potentially affect subsequent code in the caller's execution context. I don't know enough about your real use case to suggest a concrete solution, but there are typically ways to refactor your code to avoid reliance on such side effects. Here are a couple of potential solutions: class Test:
...
def set_x(self, value: str) -> str:
self.x = value
return value
t = Test()
x = t.x
if x is None:
x = t.set_x("hello") x = t.x
if x is None:
t.set_x(x := "hello") |
Beta Was this translation helpful? Give feedback.
Static type checkers narrow the type of expressions locally within an execution context (e.g. within a function body or within the module's global scope). They do not track side effects by other execution contexts. In your example, the
set_x
method has a side effect that happens to change the value of the local expressiont.x
. You'll see the same behavior in other Python type checkers like mypy as well as type checkers in other languages like TypeScript.In general, it's best to avoid writing methods that have side effects like this, especially when they potentially affect subsequent code in the caller's execution context.
I don't know enough about your real use case to suggest a concrete…