I want to check if some line ends with any substring defined in some list. Is there a better way than following one?

endsA = ['foo', 'bar', 'fur', 'beer']
endsB = ['door', ... ]

def checkEnds(line, ends):
    for end in ends:
        if line.endswith(end):
            return True

....

if checkEnds(line, endsA):
    do_something()

Yes, there is! Generator comprehensions and the any function to the rescue.

def checkEnds(line, ends):
    return any(line.endswith(end) for end in ends)

Just like the original code, this only checks as many line.endswith(end) as necessary, returning True on the first True result.

  • Thank you (+1). I am wondering will endsWith perform to each element of ends or till first True? – Loom Mar 30 '16 at 15:40
  • 2
    Written in this form, it will stop as soon as possible. – Sjoerd Job Postmus Mar 30 '16 at 16:13
  • 1
    @SjoerdJobPostmus: That would be a good detail to have as a note in your answer, IMO. +1 – Zach Gates Mar 30 '16 at 21:52
  • @ZachGates: Added it to the answer instead. – Sjoerd Job Postmus Mar 31 '16 at 7:48

@Sjoerd's solution using any() is definitely the way to go.

I'd also like to point out a recommendation in PEP 8:

  • Be consistent in return statements. Either all return statements in a function should return an expression, or none of them should. If any return statement returns an expression, any return statements where no value is returned should explicitly state this as return None, and an explicit return statement should be present at the end of the function (if reachable).

In your case, this means that if you were to keep your for loop, you should also write an explicit return False instead of returning None by falling off the end of the function.

I would also suggest a better name for your function: any_suffix_matches(suffixes, string). This name leads to more readable code in the caller, and clearly conveys the idea that the function is a true-false test. It also conforms to the PEP 8 naming convention.

if any_suffix_matches(endsA, line):
    do_something()

Note that I've also swapped the order of the parameters. Since "contains any of these suffixes" is the test, and the test is applied to some string, the suffixes should be placed closer to the function name. You see this design convention in re.search(pattern, string), for example.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.