Where is the logical 'or' equivalent in the 'operator' module?

Viewed 259

Adding or multiplying a large list of numbers in Python can elegantly be done by folding the list with the addition or multiplication operator:

import functools, operator
lst = range(1,100)
sum  = functools.reduce(operator.add, lst, 0)
prod = functools.reduce(operator.mul, lst, 1)    

This needs the function equivalents of the operators + and * which are provided by the operator module as operator.add and operator.mul, respectively.

If I want to use the same idiom with the operator or:

ingredients       = ['onion', 'celery', 'cyanide', 'chicken stock']
soup_is_poisonous = functools.reduce(operator.or, map(is_poisonous, ingredients), False)

... then I discover that operator doesn't have a function equivalent of the logical and and or operators (though it has one for logical not)

Of course, I can trivially write one that works:

def operator_or(x,y):
  return x or y

But I wonder: why are there no operator.or and operator.and in operator? Bitwise and and or are there, but not the logical ones.

Of course this is just a minor annoyance, and the answer may well be the same as with the missing identity function: that it is easy to write one. But this holds for * and + as well, so why the difference?

2 Answers

To wrap up all your helpful answers and comments, in order of somewhat decreasing (to me) convincingness:

  1. the addition of operator.or would break an important promise made by the module

    For all operators <op> that have function equivalents operator.op in the operator module, it is the case that a <op> b is equivalent to (i.e. can always, without changing program behaviour, replace or be replaced by) operator.op(a, b). This equivalence is actually mentioned in the module docstring. This is impossible to do for the operators and and or as their evaluation is short-circuiting while Python function calls are always evaluated after all of their arguments are.

  2. On the values True and False, | and &, hence also the existing (bitwise) operator.and_ and operator.or_ already return the same results (if they return at all, that is) as or and and.

    If is_poisonous() returns either True of False (not an unreasonable requirement), I could use

    soup_is_poisonous = reduce(operator.or_, map(is_poisonous, ingredients), False)
    

    in the example from the original question. However, many Python programs conveniently use any "truthy" value as True in idioms like

    your_model_T_color = "black" or any_color_you_like
    

    using | or operator.or_ instead of or here will result in a TypeError or, even worse, some unexpected value (if the operands are ints)

  3. The functions any and all can be used instead of functools.reduce(operator.or, ....)

    I'm not convinced by this argument: operator functions are used in many more contexts than as a first argument to reduce. Moreover, any always returns either True or False, not the first truthy value:

    any([0,0,0,5,6,7]) # returns True
    reduce(lambda x, y: x or y, [0,0,0,5,6,7]) # returns 5
    

    so any and reduce(operator.or would not really be equivalent

  4. any([x,y]) does the same (and more, as it accepts iterables) as operator.or(x,y) would.

    That is not quite true (see above), any([0,5]) returns True while operator.or(0,5) would return 5. Moreover, the number of arguments matters greatly if we use a function as an argument to another function like reduce()

all is short-circuiting logical-and.
any is short-circuiting logical-or.

No need to put versions that take exactly two arguments (instead of an iterable) into the operator module, I guess.

Related