17

I would like to make bool binary operations using the magic methods for these operators. For example, I can get a < b as getattr(a, '__lt__')(b) or a == b as getattr(a, '__eq__')(b).

Can I get a in b and a is b in such a way?

Dimitris Fasarakis Hilliard
  • 119,766
  • 27
  • 228
  • 224
nik
  • 215
  • 1
  • 7

3 Answers3

25

For in, the correct dunder method is __contains__.

There is no method for is, because this is equivalent to id(a) == id(b). It compares the actual object ID used under the hood by Python, so is used to compare object identity, not object contents. Overwriting it within a class would break Python's object model, so it is not allowed.

SethMMorton
  • 36,611
  • 12
  • 59
  • 76
15

in is __contains__ and is does not have a dunder method. I strongly suggest you use the functions in the operator module:

a < b  => operator.lt(a, b)
a == b => operator.eq(a, b)
a in b => operator.contains(a, b)
a is b => operator.is_(a, b)
Dan D.
  • 67,516
  • 13
  • 93
  • 109
  • Why would the `operator` module be preferable to the usual way? – Mark Ransom Dec 05 '16 at 18:24
  • It wouldn't be but if you need function versions of the operators the operator module is preferable to accessing dunder methods or writing your own wrappers. – Dan D. Dec 05 '16 at 18:25
  • 3
    I read `getattr(a, '__lt__')(b)` as asking for the function version of `a < b` which is `operator.lt(a, b)`. You can pass around `operator.lt`. I saw no attempt at definition of a class in which you would define the dunder methods. – Dan D. Dec 05 '16 at 18:28
  • I agree with you now. Your answer definitely provides a superior alternative to using `getattr`. – SethMMorton Dec 05 '16 at 18:29
  • If you want `operator.is_` with the first argument already filled in, like `getattr(a, '__lt__')` already has `a` as its first argument) you can use `functools.partial`. `partial(operator.is_, a)` is a function that only takes one argument. – RemcoGerlich Dec 06 '16 at 08:22
2

__contains__ is correct for in, with fall-back options if __contains__ isn't defined being __iter__ and __getitem__.

I am not really sure why you'd need to use getattr for is though; is is "defined" for every object in Python. There's no need to go through operator._is or (trying and failing) through getattr.

See the documentation on built-in types:

The behavior of the is and is not operators cannot be customized; also they can be applied to any two objects and never raise an exception.

(Emphasis mine)

According to the snippets you provided, which just grab a function and call it using getattr(a, "function")(b), you already have the names of the objects you need to evaluate, just use is immediately; it is always available.

Dimitris Fasarakis Hilliard
  • 119,766
  • 27
  • 228
  • 224