Python logical or operator acting strange

Question:

I am trying to populate a list called images with files that have the extensions '.png', '.jpg' or 'jpeg' in a one line for loop. I tried this by using the logical or operator but that only resulted in an empty list.

images = [i for i in os.listdir('.') if i[-4:] == ('.png' or '.jpg' or 'jpeg')]

How do I check to see if '.png', '.jpg' or 'jpeg' are the last 4 characters of the file while keeping it all in a one line for loop?

Asked By: user883807

||

Answers:

Use the in operator:

images = [i for i in os.listdir('.') if i[-4:] in ('.png', '.jpg', 'jpeg')]

Even better, instead of just checking the last 4 characters use os.path.splitext():

images = [i for i in os.listdir('.') if os.path.splitext(i)[1] in ('.png', '.jpg', '.jpeg')]

In case you wonder why your code did not work: ('.png' or '.jpg' or 'jpeg') == '.png' since it’s the first value that’s not falsy.

Answered By: ThiefMaster

As you wrote it, python evaluates ('.png' or '.jpg' or 'jpeg'), and compares i[-4:] to the result.

What you probably want is what @ThiefMaster suggests: the in operator.

Answered By: phimuemue

The expression (‘.png’ or ‘.jpg’ or ‘jpeg’) is actually evaluated to just ‘.png’ first, and is probably not behaving as you expect.

You can instead try:

images = [i for i in os.listdir('.') if i[-4:] in ('.png', '.jpg', 'jpeg')]
Answered By: Christian

You can use the fnmatch module for this purpose.

>>> extensions = ['*.jpg','*.png','*.jpeg']
>>> [file for file in os.listdir(".") for ext in extensions if fnmatch.fnmatch(file,ext)]
['1.png', '2.jpg', '3.jpeg']
Answered By: RanRag

Using or is a boolean expression which checks if any value in the expression evaluates to True.

i[-4:] == ('.png' or '.jpg' or 'jpeg')

evaluates if i[-4:] is equal to:

('.png' or '.jpg' or 'jpeg') 

which checks if each value is True and returns the first True value or the last value if there are no True values. In this case they all evaluate to True as only empty strings evaluate to False so '.png' is the result of the expression.

You can fix this by doing

[i for i in os.listdir('.') if i[-4:] in ('.png', '.jpg', 'jpeg')]

Or a better way

[i for i in os.listdir('.') if os.path.splitext(i)[1] in ('.png','.jpg','.jpeg')]
Answered By: jamylak

Other answers have suggested using in here, but another way that you might prefer is to use the str.endswith() method:

extensions = '.png', '.jpg', '.jpeg'
images = [i for i in os.listdir('.') if i.endswith(extensions)]

This has the advantage that you don’t have to fudge the match on .jpeg: you can check for extensions of any length (even storing the list somewhere separate from the rest of the code) without having to worry about breaking the code if any are shorter or longer than the others.

Answered By: Duncan