Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does str.startswith really work?

Tags:

I've been playing for a bit with startswith() and I've discovered something interesting:

>>> tup = ('1', '2', '3') >>> lis = ['1', '2', '3', '4'] >>> '1'.startswith(tup) True >>> '1'.startswith(lis) Traceback (most recent call last):   File "<stdin>", line 1, in <module> TypeError: startswith first arg must be str or a tuple of str, not list 

Now, the error is obvious and casting the list into a tuple will work just fine as it did in the first place:

>>> '1'.startswith(tuple(lis)) True 

Now, my question is: why the first argument must be str or a tuple of str prefixes, but not a list of str prefixes?

AFAIK, the Python code for startswith() might look like this:

def startswith(src, prefix):     return src[:len(prefix)] == prefix 

But that just confuses me more, because even with it in mind, it still shouldn't make any difference whether is a list or tuple. What am I missing ?

like image 239
Cajuu' Avatar asked Jul 15 '17 11:07

Cajuu'


People also ask

How does Python startsWith work?

startswith() Return Value startswith() method returns a boolean. It returns True if the string starts with the specified prefix. It returns False if the string doesn't start with the specified prefix.

How does startsWith work in Java?

The startsWith() method checks whether a string starts with the specified character(s). Tip: Use the endsWith() method to check whether a string ends with the specified character(s).

Can I use string startsWith?

JavaScript String startsWith()The startsWith() method returns true if a string starts with a specified string. Otherwise it returns false . The startsWith() method is case sensitive.

What is the purpose of startsWith () in JavaScript?

The startsWith() method determines whether a string begins with the characters of a specified string, returning true or false as appropriate.


1 Answers

There is technically no reason to accept other sequence types, no. The source code roughly does this:

if isinstance(prefix, tuple):     for substring in prefix:         if not isinstance(substring, str):             raise TypeError(...)         return tailmatch(...) elif not isinstance(prefix, str):     raise TypeError(...) return tailmatch(...) 

(where tailmatch(...) does the actual matching work).

So yes, any iterable would do for that for loop. But, all the other string test APIs (as well as isinstance() and issubclass()) that take multiple values also only accept tuples, and this tells you as a user of the API that it is safe to assume that the value won't be mutated. You can't mutate a tuple but the method could in theory mutate the list.

Also note that you usually test for a fixed number of prefixes or suffixes or classes (in the case of isinstance() and issubclass()); the implementation is not suited for a large number of elements. A tuple implies that you have a limited number of elements, while lists can be arbitrarily large.

Next, if any iterable or sequence type would be acceptable, then that would include strings; a single string is also a sequence. Should then a single string argument be treated as separate characters, or as a single prefix?

So in other words, it's a limitation to self-document that the sequence won't be mutated, is consistent with other APIs, it carries an implication of a limited number of items to test against, and removes ambiguity as to how a single string argument should be treated.

Note that this was brought up before on the Python Ideas list; see this thread; Guido van Rossum's main argument there is that you either special case for single strings or for only accepting a tuple. He picked the latter and doesn't see a need to change this.

like image 69
Martijn Pieters Avatar answered Sep 20 '22 20:09

Martijn Pieters