Home > Software design >  Is there a complement to `operator.contains`?
Is there a complement to `operator.contains`?

Time:03-30

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.

CodePudding user response:

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.

CodePudding user response:

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