¿Cómo funciona realmente str.startswith?

He estado jugando un poco con startswith() y he descubierto algo interesante:

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

Ahora, el error es obvio y convertir la lista en una tupla funcionará bien como lo hizo en primer lugar:

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

Ahora, mi pregunta es: ¿por qué el primer argumento debe ser str o una tupla de prefijos str, pero no una lista de prefijos str?

AFAIK, el código de Python para startswith() podría verse así:

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

Pero eso solo me confunde más, porque aun con eso en mente, no debería hacer ninguna diferencia si es una lista o una tupla. Qué me estoy perdiendo ?

Técnicamente no hay razón para aceptar otros tipos de secuencia, no. El código fuente hace aproximadamente esto:

 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(...) 

(donde tailmatch(...) hace el trabajo de coincidencia real).

Así que sí, cualquier iterable haría por eso for bucle. Pero, todas las demás API de prueba de cadena (así como isinstance() y issubclass() ) que toman múltiples valores también aceptan solo tuplas, y esto le dice a usted como usuario de la API que es seguro asumir que el valor ganó ” ser mutado No se puede mutar una tupla, pero el método podría, en teoría, mutar la lista.

También tenga en cuenta que normalmente prueba un número fijo de prefijos o sufijos o clases (en el caso de isinstance() y issubclass() ); La implementación no es adecuada para una gran cantidad de elementos. Una tupla implica que tiene un número limitado de elementos, mientras que las listas pueden ser arbitrariamente grandes.

A continuación, si cualquier tipo de secuencia o iterable sería aceptable, entonces eso incluiría cadenas; una sola cadena es también una secuencia. ¿Entonces, un argumento de una sola cadena debe tratarse como caracteres separados o como un solo prefijo?

En otras palabras, es una limitación del autodocumento que la secuencia no se modificará, es coherente con otras API, conlleva una implicación de un número limitado de elementos con los que realizar pruebas y elimina la ambigüedad en cuanto a cómo una única cadena El argumento debe ser tratado.

Tenga en cuenta que esto se mencionó anteriormente en la lista de Ideas de Python; ver este hilo ; El argumento principal de Guido van Rossum es que o bien es un caso especial para cadenas sencillas o solo para aceptar una tupla. Escogió este último y no ve la necesidad de cambiar esto.

Esto ya se ha sugerido en Python-ideas hace un par de años: str.startswith toma cualquier iterador en lugar de solo tuple y GvR tenía esto para decir :

El comportamiento actual es intencional, y la principal razón es la ambigüedad de las cadenas en sí mismas que son iterables. Dado que startswith() casi siempre se llama con un literal o tupla de literales de todos modos, veo poca necesidad de extender la semántica.

Además de eso, no parecía haber una motivación real de por qué hacer esto.

El enfoque actual mantiene las cosas simples y rápidas, unicode_startswith (y endswith ) comprueba un argumento de tupla y luego una cadena uno. A continuación, llaman tailmatch en la dirección apropiada. Esto es, sin duda, muy fácil de entender en su estado actual, incluso para los desconocidos del código C.

Agregar otros casos solo conducirá a un código más abultado y complejo para un pequeño beneficio, mientras que también requerirá cambios similares a cualquier otra parte del objeto Unicode.

En una nota similar, aquí hay un extracto de una charla del desarrollador central, Raymond Hettinger , que str.startswith las opciones de diseño de API con respecto a ciertos métodos de cadena, incluidos los cambios recientes en la firma str.startswith . Si bien menciona brevemente este hecho de que str.startswith acepta una cadena o tupla de cadenas y no expone, la charla es informativa sobre las decisiones y los puntos str.startswith que tanto los desarrolladores principales como los colaboradores han tratado de llevar a la API actual.