325

What is the pythonic way of writing the following code?

extensions = ['.mp3','.avi'] file_name = 'test.mp3' for extension in extensions: if file_name.endswith(extension): #do stuff 

I have a vague memory that the explicit declaration of the for loop can be avoided and be written in the if condition. Is this true?

1
  • 5
    Though this question is well answered, perhaps the author originally thought of if any((file_name.endswith(ext) for ext in extensions)). Commented Mar 2, 2017 at 9:37

7 Answers 7

671

Though not widely known, str.endswith also accepts a tuple. You don't need to loop.

>>> 'test.mp3'.endswith(('.mp3', '.avi')) True 
Sign up to request clarification or add additional context in comments.

6 Comments

do you know why it won't accept a list but does a tuple? just curious
@falsetru The link in the answer does not explicitly answer that question. It only mentions that it can accept tuples, but not why it cannot accept lists. Since they are both sequences, the only difference I can potentially see is that lists are mutable, while tuples are immutable. I may be wrong, but I can't see any other reason why that is explicitly stated.
If you want to check if a string ends with a letter: import string; str.endswith(tuple(string.ascii_lowercase))
just a note, endswith accepts tuple only for python 2.5 and above
@ilyail3: I suspect the goal is to push people towards efficient constructs. 99% of the time, the suffixes to test are constant string literals. If you put them in a list, the CPython optimizer (not knowing endswith won't store/mutate them) has to rebuild the list on every call. Put them in a tuple, and the optimizer can store off the tuple at compile time and just load it from the array of constants cheaply on each call. Similar sort of nudge to the one you get from using sum on an iterable of strings; it would work either way, but the code would be slower the wrong way.
|
69

Just use:

if file_name.endswith(tuple(extensions)): 

Comments

9

another way which can return the list of matching strings is

sample = "alexis has the control" matched_strings = filter(sample.endswith, ["trol", "ol", "troll"]) print matched_strings ['trol', 'ol'] 

1 Comment

in python>3.8 it should be : print(list(matched_strings))
7

There is two ways: regular expressions and string (str) methods.

String methods are usually faster ( ~2x ).

import re, timeit p = re.compile('.*(.mp3|.avi)$', re.IGNORECASE) file_name = 'test.mp3' print(bool(t.match(file_name)) %timeit bool(t.match(file_name) 

792 ns ± 1.83 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

file_name = 'test.mp3' extensions = ('.mp3','.avi') print(file_name.lower().endswith(extensions)) %timeit file_name.lower().endswith(extensions) 

274 ns ± 4.22 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Comments

2

I just came across this, while looking for something else.

I would recommend to go with the methods in the os package. This is because you can make it more general, compensating for any weird case.

You can do something like:

import os the_file = 'aaaa/bbbb/ccc.ddd' extensions_list = ['ddd', 'eee', 'fff'] if os.path.splitext(the_file)[-1] in extensions_list: # Do your thing. 

Comments

1

I have this:

def has_extension(filename, extension): ext = "." + extension if filename.endswith(ext): return True else: return False 

1 Comment

You mean return filename.endswith(ext) ? :P
0

Another possibility could be to make use of IN statement:

extensions = ['.mp3','.avi'] file_name = 'test.mp3' if "." in file_name and file_name[file_name.rindex("."):] in extensions: print(True) 

1 Comment

@Rainald62, index should be rindex in that case.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.