22

I would like to write a statement in python with logical implication. Something like:

if x => y:
  do_sth()

Of course, I know I could use:

if (x and y) or not x:
  do_sth()

But is there a logical operator for this in python?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
running.t
  • 5,329
  • 3
  • 32
  • 50

8 Answers8

38

p => q is the same as not(p) or q, so you could try that!

Juan Pablo Rinaldi
  • 3,394
  • 1
  • 20
  • 20
  • 1
    And that is simplier than (x and y) or not x. Thanks – running.t May 06 '13 at 19:39
  • 7
    The TTL agrees - but it's not necessarily so easy to see in code, although simpler than the original. A function - i.e. `implies(x, y)` - might help with transferring the idea more, if such a construct occurs often enough to warrant a name. – user2246674 May 06 '13 at 19:39
  • 5
    @user2246674 Agreed, I would recommend making this a function for clarity. – Gareth Latty May 06 '13 at 19:42
  • https://en.wikipedia.org/wiki/Material_nonimplication also equivalent to `p and not(q)` – Sylvain Jan 15 '19 at 10:36
  • 2
    One potential gotcha if you make an `implies(x, y)` function: because of Python's strict evaluation, the consequent `y` will always be evaluated (the operation will *not* be short-circuiting). You would think this would be obvious, but I just spent 30 minutes scratching my head over this problem. – user2846495 Apr 05 '21 at 01:12
18

Just because it's funny: x => y could be bool(x) <= bool(y) in python.

Adam Sosnowski
  • 1,126
  • 9
  • 7
  • 10
    And this is (finally) conclusive proof that `True` should be `-1` and `False` should be `0` for booleans! (Instead of the current Python convention of `True == 1`.) Because then we'd have `x => y` matching `y <= x` (which looks like a right-to-left implication) for booleans. – Mark Dickinson Oct 07 '15 at 18:44
8

There is a converse implication operator:

if y ** x:
  do_sth()

This reads: If y is implied by x.

Credits to https://github.com/cosmologicon/pywat

David Zaragoza
  • 119
  • 1
  • 6
  • Yes. This is exactly what I was looking for. And it looks like this converse implication is undocumented, so @Latty 's answer basically is incorrect. – running.t Nov 18 '15 at 10:08
  • 8
    @running.t This is something that happens to have the same effect as `x => y`, but is not an operator for that purpose. This is the power operator, and is not a logical operator, but a numerical one. It does not return `True` or `False`, but a number. This is slower, and could potentially introduce bugs, not to mention being incredibly unclear and hard to read. I would *highly* recommend against ever doing this, and instead would use `not(p) or q` as per [Juampi's answer](http://stackoverflow.com/a/16405931/722121). – Gareth Latty Nov 22 '15 at 14:02
  • I would have liked to have a bitwise version of this too. – Alexey Jul 12 '19 at 08:37
  • @GarethLatty, a "purpose" of an operator only makes sense for a particular class/type (operators can and should be overloaded). Note by the way that the `+` operator is arguably not for the purpose of doing `"Hell" + "o"`. Regarding the power operator `**` for Booleans that is not properly overloaded and does not return `True`/`False`, IMO this is a Python's bug or misfeature. If `A` and `B` are two sets, then `A^B` (`A` to the power `B`) is the standard notation for the set of functions `B -> A`. Under Curry–Howard correspondence, a function `B -> A` represents a proof of `B => A`. – Alexey Jul 12 '19 at 09:00
  • @Alexey "Operators can and should be overloaded." is a strong statement. Operators are hard to search for and rely on the user's perception of the operator, so should generally be avoided in favour of functions where the meaning isn't *very* clear to most people. The vast majority of people reading that code will find it less clear than the alternative, and—regardless of it being a bug or intentionally not done—it isn't implemented efficiently. You can argue that shouldn't be the case, but it definitely is at the moment. Given that, I'd highly recommend against it. – Gareth Latty Jul 12 '19 at 09:32
  • @GarethLatty, ok, maybe operators should not *always* be overloaded, maybe even `"Hell" + "o"` is a design mistake. However, interpreting `a^b` as `b => a` for logical values seems fairly reasonable to me. About the `**` for Booleans supposedly being slower, i could not confirm it. I've just tried to compare `True or not True` with `True**True`, and `True**True` was almost twise faster (measured with `%timeit` in IPython, Python 3.7.3). – Alexey Jul 12 '19 at 13:41
  • @Alexey While there are certainly arguments against `+` for string concat, `+` meaning an addition makes a lot of sense to most people instinctively. Maybe to those with the right background `^` is clear, but I would argue that is very niche at best. If your project is aimed at the right crowd, maybe it's more reasonable. Slower, I accept may well not be true (particularly any more), that was a long time ago. I would argue that the micro-optimisation here is the least important aspect, and readability should trump that unless it's a specific bottleneck you have identified. – Gareth Latty Jul 12 '19 at 13:46
6

Your question asks if there is a single logical operator for this in Python, the simple answer is no: The docs list boolean operations, and Python simply doesn't have anything like that.

Obviously, as Juampi's answer points out, there are logically equivalent operations that are a little shorter, but no single operators as you asked.

Community
  • 1
  • 1
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • Please take a look at [this](http://stackoverflow.com/questions/16405892/is-there-an-implication-logical-operator-in-python/33768787#33768787) answer. It looks like not everything can be found in [docs](http://docs.python.org/3.3/reference/expressions.html#boolean-operations). – running.t Nov 18 '15 at 10:11
  • 1
    @running.t It is not in the docs because that answer is wrong - there is no such operator, instead, it is an abuse of another operator that happens to produce the same result. The end result of using that would be horribly unlcear, inefficient code which may introduce bugs. – Gareth Latty Nov 22 '15 at 14:03
2

Additional details based on what I have found here and there as I was looking for an implication operator : you can use a clever hack to define your own operators. Here is a running example annotated with sources leading me to this result.

#!/usr/bin/python

# From http://code.activestate.com/recipes/384122/ (via http://stackoverflow.com/questions/932328/python-defining-my-own-operators)
class Infix:
    def __init__(self, function):
        self.function = function
    def __ror__(self, other):
        return Infix(lambda x, self=self, other=other: self.function(other, x))
    def __rlshift__(self, other):
        return Infix(lambda x, self=self, other=other: self.function(other, x))
    def __or__(self, other):
        return self.function(other)
    def __rshift__(self, other):
        return self.function(other)
    def __call__(self, value1, value2):
        return self.function(value1, value2)

from itertools import product

booleans = [False,True]

# http://stackoverflow.com/questions/16405892/is-there-an-implication-logical-operator-in-python
# http://jacob.jkrall.net/lost-operator/
operators=[
    (Infix(lambda p,q: False),                  "F"),
    (Infix(lambda p,q: True),                   "T"),
    (Infix(lambda p,q: p and q),                "&"),
    (Infix(lambda p,q: p or q)           ,      "V"),
    (Infix(lambda p,q: p != q)           ,      "^"),
    (Infix(lambda p,q: ((not p) or not q)),     "nad"),
    (Infix(lambda p,q: ((not p) and not q)),    "nor"),
    (Infix(lambda p,q: ((not p) or q)),         "=>"),
    ]

for op,sym in operators:
    print "\nTruth tables for %s" % sym

    print "\np\tq\tp %s q\tq %s p" % (sym,sym)
    for p,q in product(booleans,repeat=2):
        print "%d\t%d\t%d\t%d" % (p,q,p |op| q,q |op| p)

    print "\np\tq\tr\tp %s q\tq %s r\t(p %s q) %s r\tp %s (q %s r)\tp %s q %s r" % (sym,sym,sym,sym,sym,sym,sym,sym)
    for p,q,r in product(booleans,repeat=3):
        print "%d\t%d\t%d\t%d\t%d\t%d\t\t%d\t\t%d" % (p,q,r,p |op| q,q |op| r, (p |op| q) |op| r, p |op| (q |op| r), p |op| q |op| r)
        assert( (p |op| q) |op| r == p |op| q |op| r)
SylvainD
  • 1,743
  • 1
  • 11
  • 27
2

I would argue a more readable one-liner would be

x_implies_y = y if x else True

In your original example:

if (y if x else True): do_sth()
Gilly
  • 1,739
  • 22
  • 30
1

You can use the comparison operator <= to get an implication for two variables. Examples:

A   B   A <- B  A <= B
0   0   1       1
0   1   1       1
1   0   0       0
1   1   1       1
butichex
  • 11
  • 1
  • This is wrong. Writing `A <- B` means _B implies A_, which is false when A is false end B is true, but `A <= B` is true in that case. `A <= B` can be interpreted, counterintuitively, as _A implies B_. – Feuermurmel Feb 11 '23 at 10:20
0

I found XOR to be a good solution. you can change A implies B to not A or B. Then you use xor to negate A like this

A^1 or B

Since A xor(^) 1 is equal to not A