2

I want to compare sets in a match expression in Python. So imagine you want to compare multiple variables but they do not have any order. They only have to include the same values as a whole. In this case you should use match if you have multiple combinations you want to test for the sake of readiness. That could look like the following:

match set((string1, string2, string3)):
    case set((' Hello', ' world', '!')):
        ...
    case set(('beatufiful', 'fair', 'nice')):
        ...
    case set(('extremly', 'hyper', 'strongly')):
        ...

However, this approach does not work and now I fear I it may not be possible to compare sets this way. Comparing through '{'...',...}' expressions gives a syntax error. However comparing the following way works:

match 1:
    case 1 if set((' Hello', ' world', '!')) == set((string1, string2, string3)):
        ...
    case 1 if set(('beautiful', 'fair', 'nice')) == set((string1, string2, string3)):
        ...
    case 1 if set(('extremely', 'hyper', 'strongly')) == set((string1, string2, string3)):
        ...

(I tested it accurately, for every (special) occasion with unittest.)

But this is not the best approach for the unnecessary code.

So how do I get to a cleaner variant?

5
  • Why not just use if/elif? Commented Oct 27, 2024 at 18:23
  • @Barmar because peps.python.org/pep-0622/#rationale-and-goals. it is more concise. Also stackoverflow.com/questions/67961895/… Commented Oct 27, 2024 at 18:25
  • You are not trying to match the structure of the data. That's what that rationale would be about. Commented Oct 27, 2024 at 18:27
  • Sets aren't really structured in the way match statements aim to exploit. Commented Oct 27, 2024 at 18:27
  • I understand. But I am comparing 2 objects anyway Commented Oct 27, 2024 at 18:30

3 Answers 3

2

Why do you want to use structural pattern matching for this? There is no "structure" to match, so an if/elif would be more usual.

if s == {" Hello", " world", "!"}:
    ...
elif s == {"beatufiful", "fair", "nice"}:
    ...
elif s == {"extremly", "hyper", "strongly"}:
    ...

If you want to associate a bunch of sets with values, you can use a dict.

lookup = {
    frozenset((" Hello", " world", "!")): "case 1",
    frozenset(("beatufiful", "fair", "nice")): "case 2",
    frozenset(("extremly", "hyper", "strongly")): "case 3",
}

It will require using frozenset keys, because normal sets are unhashable.

Sign up to request clarification or add additional context in comments.

Comments

2

An expression after a case statement is not a normal expression, but a pattern. A set can't be used as a pattern for structural matching since it is unordered in Python and can't be looked up by keys, offering no way of mapping a value within the container to a capture variable.

If you want to match a set by a value you can instead define the value as a class variable so the value can be specified as a dotted name to prevent it from being treated as a pattern:

class Phrase:
    GREETING = {'Hello', 'world', '!'}
    PRAISE = {'beautiful', 'fair', 'nice'}
    EMPHASIS = {'extremely', 'hyper', 'strongly'}

match {'Hello', '!', 'world'}:
    case Phrase.GREETING:
        print('Greeting')
    case Phrase.PRAISE:
        print('Praise')
    case Phrase.EMPHASIS:
        print('Emphasis')

This outputs:

Greeting

Demo here

2 Comments

Ah, so that's why mapping patterns do exist? Because dicts are ordered?
Dicts offer keys for mapping. Tuples and lists offer indices for mapping. I've updated the answer with clarification then.
1

It would be possible to do this by converting the input set into a canonical tuple form so that you could use fixed values to compare against.

One possible canonical form would be sorted alphabetically:

match sorted(set((string1, string2, string3))):
    case (' Hello',' world', '!'):
        ...
    case ('beatufiful', 'fair', 'nice'):
        ...
    case ('extremly', 'hyper', 'strongly'):
        ...

(Your example comparison tuples are already sorted, but if not you would have to sort them as well.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.