What does "or" mean inside a Python list comprehension?

Viewed 72

I found the following line in the Django source code:

actions.extend(self.get_action(action) for action in self.actions or [])

The argument self.get_action(action) for action in self.actions or [] must be an iterable-valued comprehension, but I can't figure out what the or [] fragment could possibly mean.

There's no if, so the or is not being used in a boolean expression, which is the only use I think I'm familiar with in Python. Web searching returns nothing but the boolean use of or.

What is this syntax?

2 Answers

Maybe this will make it easier for you to see:

actions.extend(self.get_action(action) for action in (self.actions or []))

The value of self.actions might have been None, i.e. it's type is an Optional[List[T]]

Using the or operator will return the first value that evaluates as true, or return the last value it evaluated. This uses short circuit logic, meaning if you have multiple values chained together, it will stop evaluating the expression once it finds the first "truthy" value (see bottom example).

For basic semantics, consider this example:

>>> a = None
>>> b = [1,2,3]
>>> c = a or b
>>> c 
[1,2,3]

This is equivalent to:

a = None
b = [1,2,3]

if a:
     c = a
else:
     c = b

Some more examples:

>>> a = 1
>>> b = 2
>>> a or b
1

>>> a = 1
>>> b = None
>>> a or b
1

>>> a = None
>>> b = 2
>>> a or b
2

>>> a = None
>>> b = None
>>> a or b
None

>>> a = None
>>> b = False
>>> a or b
False

>>> a = 0
>>> b = 0
>>> c = 1
>>> a or b or c
1

Note in the last example we are able to chain multiple or calls together!

An example of short-circuiting (notice that foo(2) does not get called!):

>>> def foo(i):
...    print(f"foo: {i}")
...    return i
...
>>> foo(0) or foo(1) or foo(2)
foo: 0
foo: 1
1

It's essentially saying this:

if self.actions:
    for action in self.actions:
        actions.extend(self.get_action(action))
else: # For completeness, not actually necessary
    actions.extend([])
Related