5

I'm going through Zed's "Learn Python The Hard Way" and I'm on ex49. I'm quite confused by the following code he gives:

def peek(word_list):
    if word_list: # this gives me trouble
        word = word_list[0]
        return word[0]
    else:
        return None

The condition of the if statement is giving me trouble, as commented. I'm not sure what this means as word_list is an object, not a conditional statement. How can word_list, just by itself, follow if?

Sнаđошƒаӽ
  • 16,753
  • 12
  • 73
  • 90
11thHeaven
  • 349
  • 4
  • 8
  • 3
    FWIW, you aren't the first person to be confused by _Learn Python The Hard Way_; the order that it presents information leaves a lot to be desired. See [LPTHW Complaints](http://sopython.com/wiki/LPTHW_Complaints). I guess you may as well stay with it since you've gone so far already, but you may wish to check alternative tutorials. – PM 2Ring Mar 06 '16 at 13:22
  • 3
    I too don't like the approach of the writer, the way he *attacks* any ideas he himself doesn't like. For instance, about using python version 2 and 3 in the beginning of the book. I don't mean to antagonize anyone with this comment of mine though! – Sнаđошƒаӽ Mar 06 '16 at 13:26
  • 2
    @Shadowfax you really aren't. It had to be said. :) – idjaw Mar 06 '16 at 13:28
  • For more on an empty list (or other container object) having a boolean value of `False`, please see [Best way to check if a list is empty](http://stackoverflow.com/q/53513/4014959) – PM 2Ring Mar 06 '16 at 13:55

7 Answers7

7

The if statement applies the built-in bool() function to the expression which follows. In your case, the code-block inside the if statement only runs if bool(word_list) is True.

Different objects in Python evaluate to either True or False in a Boolean context. These objects are considered to be 'Truthy' or 'Falsy'. For example:

In [180]: bool('abc')
Out[180]: True

In [181]: bool('')
Out[181]: False

In [182]: bool([1, 2, 4])
Out[182]: True

In [183]: bool([])
Out[183]: False

In [184]: bool(None)
Out[184]: False

The above are examples of the fact that:

  • strings of length >= 1 are Truthy.
  • empty strings are Falsy.
  • lists of length >= 1 are Truthy.
  • empty lists are Falsy.
  • None is Falsy.

So: if word_list will evaluate to True if it is a non-empty list. However, if it is an empty list or None it will evaluate to False.

Dan D.
  • 73,243
  • 15
  • 104
  • 123
gtlambert
  • 11,711
  • 2
  • 30
  • 48
6

He is checking if word_list is empty or not. If a list is empty and it is used in a conditional statement, it is evaluated to False. Otherwise, it is evaluated to True.

word_list = ['some value']
if word_list:
    # list is not empty do some stuff
    print "I WILL PRINT"


word_list = []
if word_list:
    # list is empty
    print "I WILL NOT PRINT"

In the above code, only the first snippet will print.

See the following reference: https://docs.python.org/2/library/stdtypes.html#truth-value-testing

krato
  • 1,226
  • 4
  • 14
  • 30
5

word_list is a list and when you use it for an if statement condition you check word_list is empty or not :

word_list = []
bool(word_list)   #  False
if word_list :
    print "I'm not empty"  # would not printed
word_list = ['a']
bool(word_list)  # True
if word_list :
    print word_list[0] # 'a'

as Mad Physicist said even None elements in a list means that it's not empty:

word_list = [None]
bool(word_list) # True
ᴀʀᴍᴀɴ
  • 4,443
  • 8
  • 37
  • 57
  • I actually meant that the list itself being `None` would evaluate to `False`. The `if` is performing two checks. You're a step ahead of me. – Mad Physicist Mar 06 '16 at 13:04
5

What is required for an if block is just something that can be evaluated either to True or to False. A conditional evaluates directly to one of those, but there are other objects that can be converted. To see what any given object is, you can use bool:

>>> mylist = []
>>> bool(mylist)
False
>>> mylist = [4, 3, 6]
>>> bool(mylist)
True

You see, a list is False if it is empty, but True otherwise. Therefore, the if word_list: block will be evaluated if word_list is nonempty. Strings also are False if they are empty, but True otherwise. Same thing with tuples, dictionaries, sets. With numbers, 0 and 0.0 are False, but any other number is True. A fairly common argument to give to indicate to the function to come up with its own value is None which evaluates to False, so the if not mylist: block will be executed if mylist is empty or if mylist is None. (It would also be executed if mylist is 0, (), {}, etc.; but it's unlikely that mylist would be given those)

zondo
  • 19,901
  • 8
  • 44
  • 83
  • @MadPhysicist: Okay; I added it. – zondo Mar 06 '16 at 12:58
  • I meant that he's checking that the list is not `None`. That `if` is really performing two checks. – Mad Physicist Mar 06 '16 at 13:00
  • @MadPhysicist: It's performing any number of checks. It would also be evaluated for `0`, `()`, `{}`, etc. I made it more clear that `None` is a possible argument, though. – zondo Mar 06 '16 at 13:05
  • With a list argument, only two. Also, all objects can be converted. – Mad Physicist Mar 06 '16 at 13:07
  • `None` is not a list argument, so no. A list argument has only one. The only special thing about `None` is that it is commonly given in place of the normal argument. I don't think I contradicted that all objects can be converted; I merely gave some examples. – zondo Mar 06 '16 at 13:10
3

Take a look at this docs page for Truth Value Testing in python. You should get clear idea about your situation after reading this. Here is the relevant part for easy access.

5.1. Truth Value Testing

Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below. The following values are considered false:

  • None
  • False
  • zero of any numeric type, for example, 0, 0.0, 0j.
  • any empty sequence, for example, '', (), [].
  • any empty mapping, for example, {}.
  • instances of user-defined classes, if the class defines a __bool__() 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.

Read the first sentence (bolded) again, and note the bolded parts in the fourth rule. This relates to your question.

So, according to the 4th rule, if your word_list is empty, the condition evaluates to False, otherwise it evaluates to True.


I know you trust in the docs, but here is a code snippet to actually test the truth values for yourself. (I know it is needless to do something like this, but I am always tempted to see things with my own eyes)

def test_truth_value(arg):
    # ANY object can be evaluated for truth or false in python
    if arg: # or to be more verbose "if arg is True"
        print("'{}' is True".format(arg))
    else:
        print("'{}' is False".format(arg))

class dummy_length_zero():
    def __len__(self):
        return 0
    def __str__(self):
        return 'instance of class: "dummy_length_zero"'

class dummy_bool_False():
    def __bool__(self):
        return False
    def __str__(self):
        return 'instance of class: "dummy_bool_False"'

obj_dummy_0 = dummy_length_zero()
obj_dummy_false = dummy_bool_False()

args = [None, False, 0, 0.0, 0j, '', (), [], {}, obj_dummy_0, obj_dummy_false]

for arg in args:
    test_truth_value(arg)

And lastly, to test that last statement so objects of many types are always true, just remove the implementation of __len__() or __bool__() method from dummy_length_zero or dummy_bool_False class respectively, and check for truth.

Sнаđошƒаӽ
  • 16,753
  • 12
  • 73
  • 90
2

In python, everything has an implicit boolean value. Putting any object in an if statement directly is equivalent (but more Pythonic than) doing if bool(word_list):. None, empty sequences, empty sets, empty dicts, 0, False, 0.0 all evaluate to False. Most other objects evaluate to True. That makes if word_list: the most Pythonic way of ensuring that the list is not None or empty before accessing the first element. The long way of expressing the same thing would be if word_list is not None and len(word_list) > 0:.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
2

In Python, every expression can be evaluated to a boolean value (i.e. either True or False).

The following, basic, expressions evaluate to False

  1. The keyword False (obviously!)
  2. The keyword None
  3. The number 0 (0, 0.0 ... )
  4. empty sequence (tuple, list, string)
  5. empty mapping (dictionary)

All other expressions evaluate to True.

So, what the if statement does is evaluating the expression that follows the if keyword to either True or False, then act accordingly.

So in your specific example, if word_list matches any of the above cases it will be considered False, otherwise, it will be considered True.

[#] reference

0xcurb
  • 116
  • 6