Considera esto:
>>> '{x[1]}'.format(x="asd") 's' >>> '{x[1:3]}'.format(x="asd") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: string indices must be integers¿Cuál podría ser la causa de este comportamiento?
Un experimento basado en su comentario , verificando qué valor recibe realmente el método __getitem__ del objeto:
class C: def __getitem__(self, index): print(repr(index)) '{c[4]}'.format(c=C()) '{c[4:6]}'.format(c=C()) '{c[anything goes!@#$%^&]}'.format(c=C()) C()[4:6]Salida ( ¡Pruébelo en línea! ):
4 '4:6' 'anything goes!@#$%^&' slice(4, 6, None) Entonces, mientras que el 4 se convierte en un int , el 4:6 no se convierte en slice(4, 6, None) como en el corte habitual. En su lugar, sigue siendo simplemente la cadena '4:6' . Y ese no es un tipo válido para indexar/cortar una cadena, por lo tanto, TypeError: string indices must be integers que obtuvo.
Actualizar:
¿Está eso documentado? Bueno... No veo algo muy claro, pero @GACy20 señaló algo sutil. La gramática tiene estas reglas.
field_name ::= arg_name ("." attribute_name | "[" element_index "]")* element_index ::= digit+ | index_string index_string ::= <any source character except "]"> + Nuestro c[4:6] es field_name y estamos interesados en la parte 4:6 element_index . Creo que sería más claro si digit+ tuviera su propia regla con un nombre significativo:
field_name ::= arg_name ("." attribute_name | "[" element_index "]")* element_index ::= index_integer | index_string index_integer ::= digit+ index_string ::= <any source character except "]"> + Diría que tener index_integer e index_string indicaría más claramente que digit+ se convierte en un número entero (en lugar de permanecer como una cadena de dígitos), mientras que <any source character except "]"> + permanecería como una cadena .
Dicho esto, mirando las reglas tal como son, tal vez deberíamos pensar "¿cuál sería el punto de separar el caso de los dígitos del caso de cualquier carácter que también coincida?" y piense que el punto es tratar los dígitos puros de manera diferente, presumiblemente para convertirlos en un número entero. O tal vez alguna otra parte de la documentación incluso establece que digit o digits+ en general se convierte en un número entero.
'{x[1]}'.format(x="asd") la sintaxis [1] aquí no es la sintaxis de indexación de cadenas "normal", incluso si en este caso parece estar funcionando de la misma manera.
Está utilizando el minilenguaje de especificación de formato . El mismo mecanismo que permite pasar objetos y acceder a un atributo arbitrario dentro de la cadena formateada (por ejemplo '{x.name}'.format(x=some_object) ).
Esta sintaxis de indexación "falsa" también permite pasar objetos indexables para format y obtener directamente el elemento que desea desde dentro de la cadena formateada:
'{x[0]}'.format(x=('a', 'tuple')) # 'a' '{x[1]}'.format(x=('a', 'tuple')) # 'tuple'La única referencia (que pude encontrar, al menos) para esto en los documentos es este párrafo:
El propio field_name comienza con un arg_name que es un número o una palabra clave. Si es un número, se refiere a un argumento posicional, y si es una palabra clave, se refiere a un argumento de palabra clave con nombre. Si los arg_names numéricos en una cadena de formato son 0, 1, 2, … en secuencia, se pueden omitir todos (no solo algunos) y los números 0, 1, 2, … se insertarán automáticamente en ese orden. Debido a que arg_name no está delimitado por comillas, no es posible especificar claves de diccionario arbitrarias (por ejemplo, las cadenas '10' o ':-]') dentro de una cadena de formato. El arg_name puede ir seguido de cualquier número de expresiones de índice o atributo. Una expresión de la forma '.name' selecciona el atributo nombrado usando getattr(), mientras que una expresión de la forma '[index]' realiza una búsqueda de índice usando
__getitem__().
mientras se menciona
mientras que una expresión de la forma '[índice]' hace una búsqueda de índice usando
__getitem__().
no menciona nada acerca de que la sintaxis de corte no sea compatible.
Para mí, esto se siente como un descuido en los documentos, especialmente porque '{x[1:3]}'.format(x="asd") genera un mensaje de error tan críptico, y más aún debido a que __getitem__ ya admite el corte.