2

If foo="apple", then the following statements are equivalent:

foo.startswith("app")
foo[0:3] == "app"

In the sense that both will return "True".

Is there any reason to prefer one method over another? And by reason, I mean:

  1. Is one method significantly faster than the other?
  2. Is one method considered more or less 'Pythonic' than the other?
Lou
  • 2,200
  • 2
  • 33
  • 66
  • 3
    The second option is not generic. You need to change the `3` if you need to check with something other than `app`. You can use `len('app')` instead but do that and you'll see how bulky the check ends up looking. – rdas Nov 12 '20 at 10:41
  • Actually you should prefer the slice approach which is much faster (or at least it was) :) – Riccardo Petraglia Nov 12 '20 at 10:44
  • @RiccardoPetraglia The slice approach is [generally not faster](https://stackoverflow.com/questions/13270888/why-is-startswith-slower-than-slicing). And even if it was, readability of the code would be in most cases more important that nanosecond optimization. – Jeyekomon Aug 30 '21 at 15:03
  • @Jeyekomon Your link actually says that the slicing is faster... At the end of the day there will places where readability is more important (for example if you work in a large team) and there will be times when a faster approach is preferable (if you have to apply it to 1000s of lines -in which case you should probably consider a different approach ;))... It is not about what is the best to use, it depends only on the context. – Riccardo Petraglia Aug 30 '21 at 19:57
  • 1
    @RiccardoPetraglia Agree. Just to make things clear, the answers in the link I provided show that for long strings the `startswith` approach is actually significantly faster. Things are just not that easy. – Jeyekomon Sep 01 '21 at 07:51
  • @Jeyekomon I see what you mean... I did not check the second answer last time. Thanks for pointing it out. To be completely fair, he should have also taken into account where the "check" fails in the last case (I assume there can be differences if it fails at the first char or at the 12938456th). It would also be nice to understand what is the limit at which the startswith become faster. Unfortunately, I don't have time and I am not really using python anymore :( – Riccardo Petraglia Sep 02 '21 at 09:19

2 Answers2

3

Slicing is a special case for a general purpose feature. If you'd have to check any other slice, other then from the start or from the end of string, then slices would do. But for these two cases startswith and endswith provide better readability.

As Zen Of Python states "Simple is better than complex" and "Readability counts". So foo.startswith("app") is a better choice, except for cases when efficiency really matters.

UPD: i have conducted a quick research. It appears, there is almost no time difference between these two approaches. Computation time differs in range of 5% and is basically neglectable.

Here is the research itself:

import random
import string

def get_random_string(length):
    letters = string.ascii_lowercase
    result_str = ''.join(random.choice(letters) for i in range(length))
    return result_str
    
txt = [get_random_string(random.randint(5, 20)) for i in range(1000)]

def time_slice():
    s = datetime.now()
    for i in txt:
        check = i[0:5]
        if i[0:5] == check:
            pass
    return (datetime.now() - s).microseconds

def time_func():
    s = datetime.now()
    for i in txt:
        check = i[0:5]
        if i.startswith(check):
            pass
    return (datetime.now() - s).microseconds

>>> sum(time_slice() for i in range(100)) / 100
... 316.15

>>> sum(time_func() for i in range(100)) / 100
... 303.08
go2nirvana
  • 1,598
  • 11
  • 20
  • Excellent - you make a good argument for readability, and it's also useful to know that (at least in your tests) there's a negligible difference between the speeds. Thanks! – Lou Nov 12 '20 at 11:56
2

Yeah... There is. One shows the intention more clearly. The first is more dynamic. all you have to do is pass the argument string. while the second not only needs you to change the string but also the range 0:3 to compensate for the length of the new string. the first is just cleaner.

masonCherry
  • 894
  • 6
  • 14