Point() accepts 0 positional sub-patterns (2 given)
Asked Answered
G

1

7

I'm trying to run an example from the docs, but get the error:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: Point() accepts 0 positional sub-patterns (2 given)

Can someone explain what I doing wrong here?

class Point():
    def __init__(self, x, y):
            self.x = x
            self.y = y

x, y = 5 ,5
point = Point(x, y)
match point:
    case Point(x, y) if x == y:
        print(f"Y=X at {x}")
    case Point(x, y):
        print(f"Not on the diagonal")
Gaddi answered 19/10, 2021 at 8:39 Comment(2)
Please post your complete error message as well.Steele
my bad, I thought the if could be causing an issueBrazee
C
14

You need to define __match_args__ in your class. As pointed at in this section of the "What's new in 3.10" page:

You can use positional parameters with some builtin classes that provide an ordering for their attributes (e.g. dataclasses). You can also define a specific position for attributes in patterns by setting the __match_args__ special attribute in your classes. If it’s set to (“x”, “y”), the following patterns are all equivalent (and all bind the y attribute to the var variable):

Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)

So your class will need to look as follows:

class Point:
    __match_args__ = ("x", "y")
    def __init__(self, x, y):
        self.x = x
        self.y = y

Alternatively, you could change your match structure to the following:

match point:
    case Point(x=x, y=y) if x == y:
        print(f"Y=X at {x}")
    case Point(x=x, y=y):
        print(f"Not on the diagonal")

(Note that you don't need a both: a class with __match_args__ defined, does not need to have its arguments specified in the match-case statements.)

For full details, I'll refer readers to PEP 634, which is the specification for structural pattern matching. The details on this particular point are in the section Class Patterns.

For a better introduction or tutorial, don't use the "What's New" documentation, as it tends to provide an overview, but may skip over some things. Instead, use PEP 636 -- Structural Pattern Matching: Tutorial, or the language reference on match statements for more details.


It is mentioned in the quoted text that a dataclass will already have an ordering, and in your example, a dataclass also works fine:

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

x, y = 5, 5
point = Point(x, y)

match point:
    case Point(x, y) if x == y:
        print(f"Y=X at {x}")
    case Point(x, y):
        print(f"Not on the diagonal")
Carrollcarronade answered 19/10, 2021 at 9:4 Comment(3)
Good find. The documentation should have included a better example for Point when position-matching params. In effect, a copy-paste from the examples for __match_args__ would fail, like it has for the OP.Pantywaist
@Pantywaist True, but I think better documentation right now is to be found in the PEP 636 -- Structural Pattern Matching: Tutorial, or the language reference on match statements. The "What's New" tends to provide just a quick overview, so it will skip some things.Carrollcarronade
Fair enough. But I think that's confusing for people referring to the docs, because now they'd have to read the PEP and track it against which Python version a feature belongs to, if it changes in the future. Better for the docs to have just a one-liner and then "go read PEP 636". So the relevant info is one place. Your answer is still the right one, of course :-)Pantywaist

© 2022 - 2024 — McMap. All rights reserved.