Rendimiento de acceder a variables de clase en Python

Me pregunto si hay alguna diferencia en el rendimiento al acceder a una variable de clase (un dict) dentro de un método de la misma clase mediante:

self.class_variable_dict.add(some_key, some_value)

y

ClassName.class_variable_dict.add(some_key, some_value)

obviamente, ambos funcionarán siempre que no haya una variable de instancia con el mismo nombre, pero ¿hay algún motivo/caso de uso por el que debamos preferir uno sobre el otro?

preguntado el 22 de mayo de 12 a las 15:05

3 Respuestas

Accediendo a través de ClassName en lugar de a través de self se mostrarán ligeramente más rápido, ya que si accedes a través de self primero debe verificar el espacio de nombres de la instancia. Pero no espero que la diferencia sea significativa, a menos que tenga información de perfil que sugiera que lo es.

Por lo tanto, recomendaría usar el que crea que es más fácil de leer/comprender como humano.

Semánticamente, serán diferentes sólo si el class_variable_dict la variable se sombrea en algún lugar, en particular, si (a) self define una variable del mismo nombre; o (b) self es una instancia de una subclase de ClassName, y esa subclase (o una de sus bases que sigue siendo una subclase de ClassName) define una variable del mismo nombre. Si ninguno de ellos es cierto, entonces deberían ser semánticamente idénticos.

Edit:

Delnam tiene un buen punto: hay factores que podrían hacer que sea más rápido. Mantengo mi afirmación de que la diferencia será trivial a menos que esté en un círculo muy cerrado. Para probarlo, creé el bucle más estrecho que se me ocurrió y lo cronometré con timeit. Aquí están los resultados:

  • acceso a través de clase var: 20.226 segundos
  • acceso a través de inst var: 23.121 segundos

Según varias ejecuciones, parece que las barras de error son de aproximadamente 1 segundo, es decir, esta es una diferencia estadísticamente significativa, pero probablemente no valga la pena preocuparse. Aquí está mi código de prueba:

import timeit

setup='''
class A:
    var = {}
    def f1(self):
        x = A.var
    def f2(self):
        x = self.var

a = A()
'''
print 'access via class var: %.3f' % timeit.timeit('a.f1()', setup=setup, number=100000000)
print 'access via inst var: %.3f' % timeit.timeit('a.f2()', setup=setup, number=100000000)

contestado el 22 de mayo de 12 a las 18:05

OTOH, mirando hacia arriba self será más rápido, ya que es local y, por lo tanto, se accede desde una matriz C, mientras que ClassName es global y debe buscarse en un dict. ¡No hay forma de saber cuál es más rápido, excepto perfilando! - usuario395760

Veamos qué hacen todas las diferentes opciones.

In [1]: class Foo:
   ...:     bar = {}
   ...:     

In [2]: import dis
In [3]: dis.dis(lambda: Foo.bar.add(1,2))
  1           0 LOAD_GLOBAL              0 (Foo) 
              3 LOAD_ATTR                1 (bar) 
              6 LOAD_ATTR                2 (add) 
              9 LOAD_CONST               1 (1) 
             12 LOAD_CONST               2 (2) 
             15 CALL_FUNCTION            2 
             18 RETURN_VALUE         

In [4]: dis.dis(lambda: Foo().bar.add(1,2))
  1           0 LOAD_GLOBAL              0 (Foo) 
              3 CALL_FUNCTION            0 
              6 LOAD_ATTR                1 (bar) 
              9 LOAD_ATTR                2 (add) 
             12 LOAD_CONST               1 (1) 
             15 LOAD_CONST               2 (2) 
             18 CALL_FUNCTION            2 
             21 RETURN_VALUE 

Como puede ver, ambos estilos generan el mismo código de bytes además de crear el objeto en el segundo caso.


La otra faceta de esto es que no importa. Utiliza lo que exprese tu objetivo de la forma más precisa. Optimice solo si la velocidad es importante.

Recomendaría simplemente ir con ClassName.dict_value[key] = value

contestado el 22 de mayo de 12 a las 15:05

Si realmente le preocupa la velocidad, obviamente no coloca esto dentro de una clase, sino que tiene un módulo global _classname_dict_value, no hay ningún beneficio adicional, excepto que alguien puede acceder al dict_value fuera del alcance de su módulo. - antti haapala

@senderle: el código de bytes solo es diferente en la medida en que está creando una instancia de un objeto. - Daenyth

OK, gracias por abordar eso. Pero sigo pensando que es importante tener en cuenta que un código de bytes idéntico no necesariamente da como resultado un rendimiento idéntico. En este caso, LOAD_ATTR es una instrucción de código de bytes, pero puede ser más rápida o más lenta según las circunstancias, de la misma manera que el mismo conjunto de instrucciones de la CPU puede ser más rápida o más lenta dependiendo de si hay una falta de memoria caché. - remitente

Variables de instancia frente a variables de clase en Python

Consulte también aquí la misma pregunta en la que publiqué una prueba más amplia basada en la prueba de @Edward Loper, incluidas las variables del módulo, que son incluso más rápidas que las variables de clase.

Respondido 12 Oct 18, 11:10

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.