wtfpython icon indicating copy to clipboard operation
wtfpython copied to clipboard

Boolean operators and dictionaries are weird

Open guhcampos opened this issue 4 years ago • 2 comments

I looked around the project and did not find this, so I decided to work on a patch. I'm not sure I have a reasonable explanation for this behavior though:

Boolean operators almost never work with dicts like you would expect them to. I've seen a lot of people expect a boolean output such as:

somedict = {}
if  somedict:
  print("false")

somedict = {"some": "thing"}
if somedict:
  print("true")

But that's not what really happens. Python is really wtfckery in this:

Python 3.8.3 (default, Feb  3 2021, 10:51:05)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> da = {"a": 1 }
>>> db = {"b": 2 }
>>> dc = {}
>>> dd = {}
>>> dc and dd
{}
>>> not dc and not dd
True
>>> not dc and dd
{}
>>> da and db
{'b': 2}
>>> da and dc
{}
>>> dc and da
{}
>>> db and da
{'a': 1}
>>> da and db and dc
{}
>>> dc and db and da
{}
>>> not da and dc
False
>>> not dc and da
{'a': 1}
>>> da or db
{'a': 1}
>>> dc or dd
{}
>>> da and db or dc
{'b': 2}
>>> da or db and dc
{'a': 1}
>>> da and db
{'b': 2}
>>> bool(da and db)
True
>>> dc = { "a": 1, "c": 3 }
>>> dc and da
{'a': 1}
>>> da and dc
{'a': 1, 'c': 3}
>>> da or dc
{'a': 1}
>>> dc or da
{'a': 1, 'c': 3}
>>>

So empty dicts return False alright, but dicts with keys return the dict itself, then and and or will work in completely unexpected ways, simply returning the first mentioned dict instead of doing anything with it. I'm not sure what is exactly happening down there and never really cared to know, but it's a weird behavior that sure deserve note.

Look that casting stuff to bool seems to work, but I remember stumbling into situations it didn't work as I expected at the time.

guhcampos avatar Mar 12 '21 14:03 guhcampos

Interesting,

looks like dicts have a different implementation of the and operator. But it's weird for sure, even if there's a different implementation of and and or I'd expect them to work like intersection and union.

Thanks for sharing this btw, I'll do more research and add this in the next iteration.

Update:

It seems like this is done to facilitate the if da and db: # do something pattern. The expression (like da and db) would return dict which would either be a truthy (contains keys) or falsey (empty key). Fwiw, strings, list and other iterables seem to behave in the same way.

Now coming to the weird part (why not cast it to bool already instead of returning the iterable), the only use-case for this I can think of is

# Returning the first non-empty variable
>>> [] or [] or ['c'] or ['d']
['c']

It's unlikely though I'd have to use something like this in real world

satwikkansal avatar Mar 16 '21 10:03 satwikkansal

>>> 40 and 30 and 20 and 10
10
>>> 10 or 20 or 30 or 40
10
>>> 10 and 20 or 30 or 40
20
>>> (10 or 20) and (30 or 40)
30

It's not a different implementation of the and and or operators. It's just how they work in python.
Also, the closest data structure is a set which uses bitwise operators | and & for those operations. I think dict union is being added to python if I'm not wrong.

Let's take this for example,

x and y

x or y

This can have 4 possible situations, x is falsy, y is falsy x is falsy, y is truthy x is truthy, y is falsy x is truthy, y is truthy

Now, in case of the and operator, both of them must be truthy for the result to be True.
Therefore, if the first value, x is falsy, just return that since that is the outcome of the expression.
If the first value, x is truthy, then whatever the second value, y is, must be the outcome of the expression, so return that.

In case of the or operator, if the first value is truthy, the expression evaluates to True, if not, the expression keeps evaluating until a truthy value is found and if none of the values are truthy, the expression evaluates to False.
So, if the first value, x is truthy, return that since it's the outcome of the expression.
If the first value, x is falsy, move on to the next value, y, and return that since that is the outcome.

diptangsu avatar Jun 10 '21 14:06 diptangsu