Is there a converse to `operator.contains`?

Viewed 134

edit: I changed the title from complement to converse after the discussion below.

In the operator module, the binary functions comparing objects take two parameters. But the contains function has them swapped.

I use a list of operators, e.g. operator.lt, operator.ge.

They take 2 arguments, a and b.

I can say operator.lt(a, b) and it will tell me whether a is less than b.

But with operator.contains, I want to know whether b contains a so I have to swap the arguments.

This is a pain because I want a uniform interface, so I can have a user defined list of operations to use (I'm implementing something like Django QL).

I know I could create a helper function which swaps the arguments:

def is_contained_by(a, b):
    return operator.contains(b, a)

Is there a "standard" way to do it?

Alternatively, I can implement everything backwards, except contains. So map lt to ge, etc, but that gets really confusing.

2 Answers

If your goal is uniformity, we can go way more general than a helper function that works specifically for contains.

def flip(f):
  return lambda y, x: f(x, y)

Now flip(operator.gt) will behave (on any sane class) just like operator.lt, and flip(operator.contains) is the function you want.

If either of them posts an answer, you should accept that, but between users @chepner and @khelwood, they gave you most of the answer.

The complement of operator.contains would be something like operator.does_not_contain, so that's not what you're looking for exactly. Although I think a 'reflection' isn't quite what you're after either, since that would essentially be its inverse, if it were defined.

At any rate, as @chepner points out, contains is not backwards. It just not the same as in, in would be is_contained_by as you defined it.

Consider that a in b would not be a contains b, but rather b contains a, so the signature of operator.contains makes sense. It follows the convention of the function's stated infix operation being its name. I.e. (a < b) == operator.lt(a, b) and b contains a == operator.contains(b, a) == (a in b). (in a world where contains would be an existing infix operator)

Although I wouldn't recommend it, because it may cause confusion with others reading your code and making the wrong assumptions, you could do something like:

operator.in_ = lambda a, b: b.__contains__(a)
# or
operator.in_ = lambda a, b: operator.contains(b, a)

That would give you an operator.in_ that works as you expect (and avoids the in keyword), but at the cost of a little overhead and possible confusion. I'd recommend working with operator.contains instead.

Related