2
\$\begingroup\$

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()
\$\endgroup\$
0

2 Answers 2

9
\$\begingroup\$

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.

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

@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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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