Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this list comprehension work?

list1 = ['Hello', 10, None]
list2 = [g.lower() for g in list1 if isinstance(g, str)]
list3 = [g.lower() if isinstance(g,str) else g for g in list1]
list4 = [isinstance(g, str) and g.lower() or g for g in list1]

If I want to convert the string in list to lowercase, I can use the method in list2 and the output will be ['hello'].

In addition to this conversion, if I want to keep integers (which is 10 in this case) and None, the methods in both list3 and list4 will work and the output will be ['hello', 10, None].

My question is that I can't understand how the method in list4 works.


2 Answers

To start, writing code like this:

condition and value1 or value2

was how people implemented a ternary conditional operator in Python before the:

value1 if condition else value2

conditional expression was introduced in version 2.5 because of PEP 0308. Using the old method is now deprecated in favor of the slightly more efficient and far more readable newer method.


The old method works because of how and and or operate in Python. Instead of returning boolean results like in most other languages, these operators return values.

Doing a and b returns a if a evaluates to False; otherwise, it returns b:

>>> 0 and 1
0
>>> 1 and 0
0
>>> 1 and 2
2
>>>

Doing a or b returns a if a evaluates to True; otherwise, it returns b:

>>> 1 or 0
1
>>> 0 or 1
1
>>> 1 or 2
1
>>>

Also, in case you do not know, 0 evaluates to False while every other number evaluates to True.


Coming to your code, this:

isinstance(g, str) and g.lower() or g

is actually interpreted by Python like:

(isinstance(g, str) and g.lower()) or g

Now if isinstance(g, str) returns False (g is not a string):

(False and g.lower()) or g

False is returned by and:

False or g

and then or returns g. Thus, we avoided calling .lower() on a non-string type.

If however isinstance(g, str) returns True (g is a string):

(True and g.lower()) or g

and returns g.lower():

g.lower() or g

and then or returns g.lower(), which is fine because g is a string.


Summed up, these two expressions:

g.lower() if isinstance(g,str) else g

isinstance(g, str) and g.lower() or g

are functionally equivalent. But please use the first!! The other is terrible for readability.

Quoting the doc:

The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.

The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

Because of precedence rules, isinstance(g, str) and g.lower() or g is actually evaluated as (isinstance(g, str) and g.lower()) or g (multiplication is of higher precedence than addition).

That basically means the following:

  • if isinstance(g, str) is true, result of g.lower() will be taken
  • otherwise g will be taken

As you see, it's the same thing that you have in list3 operation.

like image 45
raina77ow Avatar answered May 08 '26 19:05

raina77ow



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!