What do boolean operations on lists mean? - python

I was going through the source of pyftpdlib and I found this:
if self.rejected_users and self.allowed_users:
raise AuthorizerError("rejected_users and allowed_users options are mutually exclusive")
rejected_users and allowed_users are lists.
What's confusing me is how the and operator operates on two lists. I'd appreciate it if someone helped me out.

All objects in Python have a boolean 'value'; they are either true or false in a boolean context.
Empty lists are false. This applies to all sequences and containers, including tuples, sets, dictionaries and strings.
Numeric 0 is false too, so 0, 0.0, 0j are all false as well, as are None and of course False itself:
>>> bool([])
False
>>> bool([1, 2])
True
>>> bool(0)
False
>>> bool('')
False
Everything else is considered true in a boolean context; so a list that is not empty is true, and two non-empty lists together with and is considered true as well.
You can make custom types look like empty containers by implementing __len__() and returning 0, or look like a number by implementing __nonzero__()* and returning False when the instance is to be the boolean equivalent of numeric zero.
Just remember that and and or shortcircuit; if the first expression locks in the result then that value is returned and the second expression is ignored altogether. For and, that means that in the expression x and y, y is ignored if x is a false value (like an empty list), because the whole expression can never be true in that case. For x or y, y is ignored if x is a true value.
These rules are all covered by the Boolean operations reference documentation.
*In Python 3, use __bool__ instead.

Empty list evaluates to False and non-empty list evaluates to True.
if list1 and list2:
is equivalent to:
if list1 is not empty and list2 is not empty:
List of falsy values in python:
None
False
zero of any numeric type, for example, 0, 0L, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a
__nonzero__() or __len__() method, when that method returns the integer zero or bool value False.
All other values are considered true — so objects of many types are always true.

If list_a is empty, then list_a and list_b will evaluate to list_a, otherwise it will evaluate to list_b. For example:
>>> [1] and [2]
[2]
>>> [] and [2]
[]

In addition to #Ashwini answer, you can use bool inbuilt function to check what a given object will evaluate to.
>>> bool([])
False
>>> bool([1,2,3])
True
>>> bool('')
False
>>> bool('hello')
True

It looks to me as though it's a logical boolean statement. In Python, when building an If statement, boolean statements have an interesting logical shorthand:
bool = True;
If bool:
return "Blah!"
Is the same as:
bool = True;
If bool==True:
return "Blah!"
The statement If *[boolean]* is a cooler way of saying If *[boolean]* is True. In that same way, the statement If *![boolean]* is a cooler way of saying If *[boolean]* is False, because ! is the logical NOT operator.It takes a little time to acclimate to this logic but once you do, it makes your code a lot nicer looking.
So why do the lists do this like a boolean value should?
Since Python doesn't have explicit type declaration, every data type has a boolean representation. For lists like rejected_users and allowed_users, if the lists are empty {}'s, it returns False and if NOT empty, it returns True. Thus, the english translation of
if self.rejected_users and self.allowed_users:
should read:
if self.rejected_users is NOT empty AND if self.allowed_users is NOT empty:
I hope this helps.

Related

Is there another way besides "all" to check if all elements values before my target element are true?

I'm trying to return true if only all the previous elements are true up to the current position.
I have it set up with all function but I don't want to code it this way
def check(lightsOnOff, light):
for light in lights[:light]:
if not on:
return False
return True
count = count + 1
In general all is a useful construct to use, I can see why it looks wrong in this expression
all(list(lightsOnOff.values())[:light])
but the smelly part is actually the list(iterable)[:number] construction, which forces construction of the whole list then truncates it.
As an important aside, if lightsOnOff is a dict (not e.g. an OrderedDict) your code will be non-deterministic (see notes at bottom).
If you don't want to create a list and slice it, you can leverage itertools:
from itertools import islince
...
all(islice(lightsOnOff.values(), n))
As a frame challenge, if your dict has an order and you know the keys, you can simply rewrite it as:
all(lightsOnOff[k] for k in keys[:light])
and if your dict has keys that are ordered and e.g. integers, just use a list?
all(listOfLights[:light])
Provided you want to implement all yourself on an arbitrary list, you can do something like:
my_list = [1, 7, 2, 1, None, 2, 3]
up_to_ix = 5
def my_all(some_list, up_to_index):
for element in some_list[:up_to_index]:
if not element:
return False
return True
my_all(my_list, up_to_ix)
The function will loop through all elements in the list up to, but excluding the some_index and if it finds at least one Falsy value, will return False, otherwise True.

How does this print syntax work? print('something', ['a', 'list'][boolean])

print('something', ['a', 'list'][boolean])
Depending on the boolean value this either prints, a or list.
I have never seen this notation before and am wondering how it works.
Python's bool is a subclass of int, where True is 1 and False is 0. isinstance(True, int) # True
As such, booleans can be used as indexes. ['a', 'list'][boolean] evaluates to ['a', 'list'][0] if boolean is False or to ['a', 'list'][1] if boolean is True
This can be abused by using conditions directly:
x = 1
print(['no', 'yes'][x > 0])
# yes
Notice the following in python
>>> True == 1
True
>>> False == 0
True
as booleans are integers (in Python). so [0,1,2][False] == 0 and [0,1,2][True] == 1
The boolean is either True or False. If you have a list mylist then mylist[0] gets you the first element and mylist[1] gets you the second element. mylist[False] means the same as mylist[0]. Now suppose mylist contains ["list", "a"]. Then ["list", "a"][False] will give you the same value as mylist[0] which is "list".
You are accustomed to seeing index notation (for example [0]) after the name of a list, as in mylist[0]. But it can just as well be used after a list literal, as in ["list", "a"][0].

Why does "[] is [ ]" evaluate to False in python

Try this in an interactive python shell.
[] is [ ]
The above returns False, why?
You created two mutable objects, then used is to see if those are the same object. That should definitely return False, or something would be broken.
You wouldn't ever want is to return true here. Imagine if you did this:
foo = []
bar = []
foo.append(42)
then you'd be very surprised if bar now contains 42. If is returned true, meaning that both [] invocations returned the exact same object, then appending to foo would be visible in the reference to bar.
For immutable objects, it makes sense to cache objects, at which point is may return true, like with empty tuples:
>>> () is () # are these two things the same object?
True
The CPython implementation has optimised empty tuple creation; you'll always get the exact same object, because that saves memory and makes certain operations faster. Because tuples are immutable, this is entirely safe.
If you expected to test for value equality instead, then you got the wrong operator. Use the == operator instead:
>>> [] == [] # do these two objects have the same value?
True
In python is does a reference equality check like [] and [] they are different objects you can check that by
print id([]),id([])
or
In [1]: id([])
Out[1]: 140464629086976
In [2]: id([])
Out[2]: 140464628521656
both will return different address and both are different object so is will always give false
[] is []
output
false
[] is like list(), if you do this:
a = list()
b = list()
clearly a and b are two completly different objects, hence:
a is b # False
like
list() is list() # False
like
[] is [] # False
The == operator compares the values of both the operands and checks for value equality. Whereas is operator checks whether both the operands refer to the same object or not.
id('') : 139634828889200
id('') : 139634828889200
id('') : 139634828889200
id([]) : 139634689473416
id([]) : 139634689054536
id([]) : 139634742570824

What does the builtin function any() do?

I did some google searching on how to check if a string has any elements of a list in it and I found this bit of code that works:
if any(i in string for i in list):
I know this works, but I don't really know why. Could you share some insight?
As the docs for any say:
Return True if any element of the iterable is true. If the iterable is empty, return False. Equivalent to:
def any(iterable):
for element in iterable:
if element:
return True
return False
So, this is equivalent to:
for element in (i in string for i in list):
if element:
return True
return False
… which is itself effectively equivalent to:
for i in list:
element = i in string
if element:
return True
return False
If you don't understand the last part, first read the tutorial section on list comprehensions, then skip ahead to iterators, generators, and generator expressions.
If you want to really break it down, you can do this:
elements = []
for i in list:
elements.append(i in string)
for element in elements:
if element:
return True
return False
That still isn't exactly the same, because a generator expression builds a generator, not a list, but it should be enough to get you going until you read the tutorial sections.
But meanwhile, the point of having any and comprehensions and so on is that you can almost read them as plain English:
if any(i in string for i in list): # Python
if any of the i's is in the string, for each i in the list: # pseudo-English
i in string for i in list
This produces an iterable of booleans indicating whether each item in list is in string. Then you check whether any item in this iterable of bools is true.
In effect, you're checking whether any of the items in the list are substrings of string.
What's going on here with:
if any(i in string for i in list):
is best explained by illustrating:
>>> xs = ["Goodbye", "Foo", "Balloon"]
>>> s = "Goodbye World"
>>> [i in s for i in xs]
[True, False, False]
>>> any([i in s for i in xs])
True
If you read the any documentaiton you'll note:
any(iterable) Return True if any element of the iterable is true.
If the iterable is empty, return False. Equivalent to:
The list comprehension should be more obvious as it constructs a list of i in s for each element of xs.
Basically (in English) you are returning any match where each sub-string exists in the search string (haystack).
It's important to note as well that any() will short circuit and end on the first True(ish) value it finds. any() can be implement in pure Python like this:
def any(iterable):
for x in iterable:
if x:
return True
return False

"in" statement behavior in lists vs. strings

In Python, asking if a substring exists in a string is pretty straightforward:
>>> their_string = 'abracadabra'
>>> our_string = 'cad'
>>> our_string in their_string
True
However, checking if these same characters are "in" a list fails:
>>> ours, theirs = map(list, [our_string, their_string])
>>> ours in theirs
False
>>> ours, theirs = map(tuple, [our_string, their_string])
>>> ours in theirs
False
I wasn't able to find any obvious reason why checking for elements "in" an ordered (even immutable) iterable would behave differently than a different type of ordered, immutable iterable.
For container types such as lists and tuples, x in container checks if x is an item in the container. Thus with ours in theirs, Python checks if ours is an item in theirs and finds that it is False.
Remember that a list could contain a list. (e.g [['a','b','c'], ...])
>>> ours = ['a','b','c']
>>> theirs = [['a','b','c'], 1, 2]
>>> ours in theirs
True
Are you looking to see if 'cad' is in any of the strings in a list of strings? That would like something like:
stringsToSearch = ['blah', 'foo', 'bar', 'abracadabra']
if any('cad' in s for s in stringsToSearch):
# 'cad' was in at least one string in the list
else:
# none of the strings in the list contain 'cad'
From the Python documentation, https://docs.python.org/2/library/stdtypes.html for sequences:
x in s True if an item of s is equal to x, else False (1)
x not in s False if an item of s is equal to x, else True (1)
(1) When s is a string or Unicode string object the in and not in operations act like a substring test.
For user defined classes, the __contains__ method implements this in test. list and tuple implement the basic notion. string has the added notion of 'substring'. string is a special case among the basic sequences.

Categories