2010-11-22 10 views
6

He mirado recientemente en el uso de list(), dict(), tuple() en lugar de [], {}, y (), respectivamente, cuando la necesidad de crear un vacío uno de de los tres. El razonamiento es que parecía más legible. Iba a pedir opiniones sobre el estilo, pero luego decidí probar el rendimiento. Hice esto:¿Por qué son list(), dict() y tuple() más lentos que [], {} y()?

>>> from timeit import Timer 
>>> Timer('for x in range(5): y = []').timeit() 
0.59327821802969538 
>>> from timeit import Timer 
>>> Timer('for x in range(5): y = list()').timeit() 
1.2198944904251618 

Probé dict(), tuple() y list() y la versión llamada a la función de cada uno era increíblemente peor que la versión sintáctica ({}[], ()) Por lo tanto, tengo 3 preguntas:

  1. ¿Por qué las llamadas a funciones son más caras?
  2. ¿Por qué hay tanta diferencia?
  3. ¿Por qué diablos se tardan 1,2 segundos para crear 5 listas vacías en mi temporizador? Sé que timeit desactiva la recolección de basura, pero eso no podría tener un efecto cuando considero que solo usé range(5). Se requieren
+0

re: estilo de opinión - Yo uso [] con bastante frecuencia. Creo que es lo suficientemente claro. La pregunta de rendimiento es interesante sin embargo. – nmichaels

+0

Hay muchas cosas que se ven raras en python hasta que te acostumbras. Siempre preferiría '[]' a 'list()' porque 'list()' podría no ser '__builtins__. List'. el comprador tenga cuidado. – SingleNegationElimination

Respuesta

18

la llamada de función requiere una búsqueda de nombre variable, seguida de una invocación de función. la función llamada luego crea una lista y la devuelve. La sintaxis lista literal consigue que el intérprete acaba de hacer una lista:

>>> import dis 
>>> foo = lambda :[] 
>>> bar = lambda :list() 
>>> dis.dis(foo) 
    1   0 BUILD_LIST    0 
       3 RETURN_VALUE   
>>> dis.dis(bar) 
    1   0 LOAD_GLOBAL    0 (list) 
       3 CALL_FUNCTION   0 
       6 RETURN_VALUE   
>>> 
0

búsquedas de alcance con el fin de encontrar dict, tuple y list, y múltiples campos de acción tienen que ser registrados con el fin de encontrarlos. Con el azúcar sintáctico, el compilador puede saber que es necesario crear un objeto específico y, por lo tanto, puede emitir el bytecode adecuado para hacerlo.

+1

'[]' no es azúcar sintáctico para nada. Es una lista literal. – SingleNegationElimination

+0

@TokenMacGuy: De hecho, es azúcar sintáctica. Las listas se pueden generar invocando 'list()', pero el compilador interpretará los corchetes en el contexto apropiado como creando una lista. –

+2

el compilador no hace * invocar 'list()' cuando encuentra '[]'. ver mi respuesta – SingleNegationElimination

2
>>> from dis import dis 

    >>> dis(lambda: list()) 
    1   0 LOAD_GLOBAL    0 (list) 
       3 CALL_FUNCTION   0 
       6 RETURN_VALUE   

    >>> dis(lambda: []) 
    1   0 BUILD_LIST    0 
       3 RETURN_VALUE   
5

Para contestar # 3.

timeit en realidad repite su programa 1 000 000 veces por defecto. Entonces, de hecho, estás creando 5 millones de listas en 1.2 segundos.

+0

gracias. Supongo que la próxima vez debería RTFM :) – orokusaki

Cuestiones relacionadas