2

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?

Darien Marks
  • 446
  • 4
  • 18
  • 1
    The `or` is not special syntax for the generator expression. It may help you to place parenthesis around `(self.actions or [])`, and then check the results of `True or []` and `False or []`. – Brian61354270 Jan 27 '21 at 01:42

2 Answers2

6

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
flakes
  • 21,558
  • 8
  • 41
  • 88
  • 2
    It could be more useful to re-iterate that `or` is a "short-circuit operator", so it stops evaluating its operands upon encountering the first operand that evaluates to "true", and it always returns the last evaluated operand's value, which could be a non-boolean value. – fountainhead Jan 27 '21 at 01:52
  • @fountainhead I updated my answer with an example. Thanks for pointing that out! – flakes Jan 27 '21 at 01:58
  • I have never seen this before in my life, but it's a really good idea. Thanks! – Darien Marks Jan 27 '21 at 12:06
0

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([])
goalie1998
  • 1,427
  • 1
  • 9
  • 16