I'm puzzled by how from ... import ...
, class constructor and match...case
in Python interact between each other.
Given there are two modules, foo
and bar
. foo
imports CONST
from bar
and tries to use it in Foo
class __init__()
within match...case
construction. This gives UnboundLocalError
.
The minimal reproducer is below.
foo.py
:
from bar import CONST
class Foo:
def __init__(self, var):
print(CONST)
match var:
case CONST:
print("hit")
foo_instance = Foo(1)
bar.py
(single line only):
CONST = 1
The error is:
Traceback (most recent call last):
File "…/foo.py", line 10, in <module>
foo_instance = Foo(1)
File "…/foo.py", line 5, in __init__
print(CONST)
UnboundLocalError: local variable 'CONST' referenced before assignment
If I remove match...case
from __init()__
or if I replace it with a simple if
, the error disappears, and print(CONST)
prints 1
as I expect.
pylint
also warns about the code:
foo.py:7:17: W0621: Redefining name 'CONST' from outer scope (line 1) (redefined-outer-name)
foo.py:5:14: E0601: Using variable 'CONST' before assignment (used-before-assignment)
foo.py:7:17: C0103: Variable name "CONST" doesn't conform to snake_case naming style (invalid-name)
but I do not really understand it. What's the issue with scoping here?
Could you please explain the behaviour to me?
Python interpreter version in use: 3.10.7
.
Thanks.
CodePudding user response:
The case CONST
acts like an assignment in this case. This is because the case statements can be used to capture the values being matched as well. So python tries to create a new variable named CONST
in that scope. So the previous print(CONST)
acts like it's trying to access a variable that doesn't exist until the match is executed.
Ref: https://docs.python.org/3/tutorial/controlflow.html#match-statements
Patterns can look like unpacking assignments, and can be used to bind variables:
# point is an (x, y) tuple
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")