1

Is it possible to implement the assignment of a variable to the value of the logical "and" or "or"? and use them in a comparison expression?

logical_obj = bool()

if True:
    logical_odj = or
else:
    logical_obj = and

if 1 + 2 == 3 logical_obj 3 + 1 == 5:
    pass
  • This isn't exactly a duplicate of the marked question as `operator` does not expose functions for `and` or `or`. In a pinch bitwise operators could be used, but that would probably justify an answer. – Brian61354270 Apr 10 '20 at 00:59
  • 1
    See: https://stackoverflow.com/questions/7894653/why-doesnt-the-operator-module-have-a-function-for-logical-or – blhsing Apr 10 '20 at 01:00
  • 2
    `1 or 3 == 4` probably isn't what you want anyway, but `1 == 4 or 3 == 4`. – chepner Apr 10 '20 at 01:04
  • In that case, you probably want to use `any([1 == 4, 3==4])` or `all([1==4, 3==4])`, as desired. – chepner Apr 10 '20 at 01:05
  • (And you *can* assign the functions `any` and `all` to a variable.) – chepner Apr 10 '20 at 01:13

3 Answers3

2

and and or are operators, like + and -, and cannot be assigned to variables. Unlike + et al., there are no functions that implement them, due to short-circuting: a and b only evaluates b if a has a truthy value, while foo(a, b) must evaluate both a and b before foo is called.

The closest equivalent would be the any and all functions, each of which returns true as soon as it finds a true or false value, respectively, in its argument.

>>> any([1+2==3, 3+1==5])  # Only needs the first element of the list
True
>>> all([3+1==5, 1+2==3])  # Only needs the first element of the list
False

Since these are ordinary functions, you can bind them to a variable.

if True:
    logical_obj = any
else:
    logical_obj = all

if logical_obj([1 + 2 == 3, 3 + 1 == 5]):
    pass

The list has to be fully evaluated before the function can be called, but if the iterable is lazily generated, you can prevent expressions from being evaluated until necessary:

>>> def values():
...    yield 1 + 2 == 3
...    print("Not reached by any()")
...    yield 3 + 1 == 5
...    print("Not reached by all()")
...
>>> any(values())
True
>>> all(values())
Not reached by any()
False
chepner
  • 497,756
  • 71
  • 530
  • 681
0

No it's not possible as these are operators but you can wrap them to a function which will then be always same:

def logical_op(cond, a, b):
  if cond:
    return a or b
  else:
    return a and b`

if logical_op(True, val1, val2) == expected:
  ...

or depending on your code you can apply De Morgan's law and convert and to or (and vice versa) by negation.

blami
  • 6,588
  • 2
  • 23
  • 31
0

This is a bit of a doozy. One possible analogue replicates the short circuiting by also implementing lazy evaluation. This means the second argument must be converted to a function; a and b would convert into sc_and(a, lambda: b).

def sc_and(value, alternative_function):
  if value:
    return alternative_function()
  else:
    return value

Iterators are Python's native means of performing such lazy evaluation, so you can use the shortcut in any and all using e.g. generator expressions:

t1s = (1, 3)
#     +
t2s = (2, 1)
#     ==
rhs = (3, 5)

any(t1+t2==rh for (t1,t2,rh) in zip(t1s,t2s,rhs))
Yann Vernier
  • 15,414
  • 2
  • 28
  • 26