2

I have defined the following classes:

@dataclass
class Var:
    name: str


@dataclass
class Val:
    value: int


@dataclass
class Op:
    operation: str
    left: 'Node'
    right: 'Node'

and created a union type of these 3

Node = Var | Val | Op

Now I want to check using structural pattern matching the following cases:

match node:
    case Var(x):
        return x
    case Val(i):
        return str(i)
    case Op(operation, (left), (right)):
        return "(" + node_to_str(left) + " " + operation + " " + node_to_str(right) + ")"

The problem is, that, in the last case, left and right could be None, giving me an error. Hence, I would like to put the constraint on left and right that they must be a Node type:

    case Op(operation, Node(left), Node(right)):
        return "(" + node_to_str(left) + " " + operation + " " + node_to_str(right) + ")"

However, using this approach, I get the error: called match pattern must be a type. What am I doing wrong?

kklaw
  • 452
  • 2
  • 4
  • 14

1 Answers1

1

Core issue

Type hints and class patterns are two different things. Writing Node = Var | Val | Op creates a type hint usable for type annotations but not for class patterns:

>>> type(Node)
<class 'types.UnionType'>

Easy solution

Create an upstream case to handle operands that are None. Then when the three-way case is encountered, the inputs are known to be non-null:

match node:
    case Var(x):
        return x
    case Val(i):
        return str(i)
    case Op(operation, None, None):
        return "Empty operation"
    case Op(operation, left, right):
        return "Non-empty operation"

Wordy solution

Instead of using the Node variable, just spell-out the OR-pattern:

match node:
    case Var(x):
        return x
    case Val(i):
        return str(i)
    case Op(operation, Var() | Val() | Op(), Var() | Val() | Op()):
        return "Non-empty operation"
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485