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 ?
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.
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).
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.
The startsWith() method determines whether a string begins with the characters of a specified string, returning true or false as appropriate.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With