2009-10-25 14 views
9

utilizo un índice negativo en campos de reemplazo para dar salida a una lista formateada, pero plantea un TypeError.The códigos son los siguientes:str.format (lista) con índice negativo no funciona en Python

 
>>> a=[1,2,3] 
>>> a[2] 
3 
>>> a[-1] 
3 
>>> 'The last:{0[2]}'.format(a) 
'The last:3' 
>>> 'The last:{0[-1]}'.format(a) 
Traceback (most recent call last): 
    File "", line 1, in 
TypeError: list indices must be integers, not str 
+0

suena como un descuido en la definición de las funciones de formato – barkmadley

+2

El error relevante en la base de datos de Python - http://bugs.python.org/issue7951. En resumen, el problema se está tratando como un error de documentación debido a los efectos secundarios de implementar esto y también al hecho de que puede conducir a un código incorrecto. – Sam

Respuesta

12

Es lo que yo llamaría un error de diseño en las especificaciones de cadena de formato. Por the docs,

element_index  ::= integer | index_string 

pero, por desgracia, -1 no es "un entero" - es una expresión. El operador unario-minus ni siquiera tiene una prioridad particularmente alta, por lo que por ejemplo print(-2**2) emite -4 - otro problema común y podría decirse que es un error de diseño (el operador ** tiene mayor prioridad, por lo que el aumento de la potencia ocurre primero, luego el signo de cambio solicitado por la prioridad inferior).

Cualquier cosa en esa posición en la cadena de formato que no es un número entero (pero, por ejemplo, una expresión) se trata como una cadena, para indexar un argumento dict - por ejemplo:

$ python3 -c "print('The last:{0[2+2]}'.format({'2+2': 23}))" 
The last:23 
No

Seguro . si vale la pena plantear una cuestión en el trac Python, pero es sin duda un comportamiento un tanto sorprendente :-(

+0

¡Interesante! En cuanto a que -2 ** 2 sea igual a -4, creo que esto es algo muy bueno, ya que esta es la convención utilizada en matemáticas. ¿Algo como element_index :: = "signed_integer" tiene sentido? – EOL

1

Hay algunos problemas aquí, una vez que comience la excavación:

el artículo en cuestión se llama " element_index "que se define como un entero.

Problema 1: a menos que los usuarios sigan el enlace de "entero" al manual de referencia del lenguaje, no sabrán que -1 se considera una expresión, no un número entero. Por cierto, cualquiera que tenga la tentación de decir "funciona como está documentado" debería ver primero el proplem 7 :-)

Solución preferida: cambie la definición para que "element_index" pueda tener un '-' opcional antes del entero.

Es un número entero, ¿no? No tan rápido ... más tarde los documentos dicen que "una expresión de la forma '[índice]' hace una búsqueda de índice usando __getitem__()"

Problema 3: Debería decir '[element_index]' (el índice no está definido).

Problema 4: No todo el mundo sabe lo que hace __getitem__(). Necesita documentos más claros.

Así que podemos usar un dict aquí, así como un número entero, ¿o sí? Sí, con un problema o dos:

¿element_index es un número entero? Sí, que trabaja con un diccionario:

>>> "{0[2]}".format({2: 'int2'}) 
'int2' 

Parece que también podemos utilizar cadenas no enteros, pero para ello se necesita una documentación más explícita (Problema 5):

>>> "{0[foo]}".format({'foo': 'bar'}) 
'bar' 

pero no podemos usar un diccionario con una llave como '2' (problema 6):

>>> "{0[2]}".format({'2': 'str2'}) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
KeyError: 2 
>>> "{0['2']}".format({'2': 'str2'}) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
KeyError: "'2'" 

problema 7: que "entero" en realidad se debe documentar para ser "decimalinteger" ...0x22 y 0b11 son tratados como str, y 010 (un "octalinteger") se trata como 10, no 8:

>>> "{0[010]}".format('abcdef') 
'a' 

Actualización:PEP 3101 cuenta la verdadera historia:
"""
Las reglas para analizar una clave de elemento son muy simples. Si comienza con un dígito, se trata como un número, de lo contrario se utiliza como una cadena.

Como las claves no están delimitadas por comillas, no es posible especificar teclas de diccionario arbitrarias (por ejemplo, las cadenas "10" o ": -]") dentro de una cadena de formato.
"" "

1

Correcto, no funciona. solución:

>>> 'The last:{0}'.format(a[-1]) 
'The last:3' 
+1

Lo que plantea la pregunta: ¿por qué molestarse en diseñar indexación de secuencia restringida, búsqueda de dict restringida y funciones de búsqueda de atributos en las cadenas de formato, cuando todo eso y más se pueden hacer en los argumentos? –

+0

Sí, me gana. –

0

menudo tomo cadenas de formato de Python como opciones de configuración - con la cadena de formato provisto de una lista específica, conocida de argumentos de palabra clave. Por lo tanto, abordar los índices de una lista de longitud variable hacia delante o hacia atrás dentro de la cadena de formato es exactamente el tipo de cosa que necesito.

que acabo de escribir este truco para hacer que el trabajo de indexación negativa:

string_to_tokenise = "Hello_world" 
tokens = re.split(r"[^A-Z\d]+", string_to_tokenise, flags=re.I) 
token_dict = {str(i) if i < 0 else i: tokens[i] for i in range(-len(tokens) + 1, len(tokens))} 
print "{thing[0]} {thing[-1]}".format(thing=token_dict) 

Resultado:

Hello world 

Así, para explicar, en lugar de pasar en la lista de fichas, se crea una diccionario con todas las claves enteras requeridas para indexar la lista de 0 a len (..) - 1, y también agrego las claves enteras negativas para indexar desde el final de -1 a - (len (..) - 1), sin embargo, estas claves se convierten de enteros en cadenas, ya que así es como el formato los interpretará.

Cuestiones relacionadas