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?
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.
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.
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')]
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']
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')]
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.
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?
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.
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.
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')]
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']
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')]
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.