Interaction between import, class and match…case in Python

Question:

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.

Answers:

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")
Answered By: rdas
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.