queryset - Tipo de Python() o__class__,== o es
group by django (4)
Quiero probar si un objeto es una instancia de una clase, y solo esta clase (sin subclases). Podría hacerlo con:
obj.__class__ == Foo
obj.__class__ is Foo
type(obj) == Foo
type(obj) is Foo
¿Hay razones para elegir una sobre otra? (diferencias de rendimiento, dificultades, etc.)
En otras palabras: a) ¿hay alguna diferencia práctica entre usar __class__
y type(x)
? b) son objetos de clase siempre seguros para comparar usando is
?
Actualización: Gracias a todos por los comentarios. Todavía estoy confundido por si los objetos de clase son o no singletons, mi sentido común lo dice, pero ha sido muy difícil obtener una confirmación (prueba con Google para "python", "class" y "unique" o "singleton") .
También me gustaría aclarar que, para mis necesidades particulares, la solución "más barata" que simplemente funciona es la mejor, ya que estoy tratando de optimizar al máximo algunas de las clases especializadas (casi llegando al punto donde lo sensato Lo que hay que hacer es eliminar Python y desarrollar ese módulo particular en C). Pero la razón detrás de la pregunta era para entender mejor el idioma, ya que algunas de sus características son un poco oscuras para que pueda encontrar esa información fácilmente. Es por eso que dejo que la discusión se extienda un poco en lugar de conformarme con __class__ is
, para poder escuchar la opinión de personas más experimentadas. ¡Hasta ahora ha sido muy fructífero!
Realicé una pequeña prueba para comparar el rendimiento de las 4 alternativas. Los resultados del perfilador fueron:
Python PyPy (4x)
type() is 2.138 2.594
__class__ is 2.185 2.437
type() == 2.213 2.625
__class__ == 2.271 2.453
Como era de esperar, is
realiza mejor que ==
para todos los casos. type()
desempeñó mejor en Python (2% más rápido) y __class__
desempeñó mejor en PyPy (6% más rápido). Es interesante notar que __class__ ==
desempeñó mejor en PyPy que type() is
.
Actualización 2: muchas personas no parecen entender lo que quiero decir con "una clase es un singleton", así que lo ilustraré con un ejemplo:
>>> class Foo(object): pass
...
>>> X = Foo
>>> class Foo(object): pass
...
>>> X == Foo
False
>>> isinstance(X(), Foo)
False
>>> isinstance(Foo(), X)
False
>>> x = type(''Foo'', (object,), dict())
>>> y = type(''Foo'', (object,), dict())
>>> x == y
False
>>> isinstance(x(), y)
False
>>> y = copy.copy(x)
>>> x == y
True
>>> x is y
True
>>> isinstance(x(), y)
True
>>> y = copy.deepcopy(x)
>>> x == y
True
>>> x is y
True
>>> isinstance(x(), y)
True
No importa si hay N objetos de tipo type
, dado un objeto, solo uno será su clase, por lo tanto, es seguro compararlos para referencia en este caso. Y como la comparación de referencias siempre será más barata que la comparación de valores, quería saber si mi afirmación anterior es válida o no. Estoy llegando a la conclusión de que sí, a menos que alguien presente pruebas en contrario.
Actualización: Gracias a todos por los comentarios. Todavía estoy confundido por si los objetos de clase son o no singletons, mi sentido común lo dice, pero ha sido muy difícil obtener una confirmación (prueba con Google para "python", "class" y "unique" o "singleton") .
Puedo confirmar que __instance__
es un singleton. Aquí está la prueba.
>>> t1=File.test()
made class
>>> t2=File.test()
made class
>>> print t1.__class__()
made class
<File.test object at 0x1101bdd10>
>>> print t2.__class__()
made class
<File.test object at 0x1101bdd10>
Como puede ver, tanto t1
como t2
imprimen el mismo valor en la memoria a pesar de que t1
y t2
tienen valores diferentes en la memoria. Aquí está la prueba de eso.
>>> print t1
<File.test object at 0x1101bdc90>
>>> print t2
<File.test object at 0x1101bdcd0>
El método __instance__
solo existe si __instance__
class "name"(object):
Si usa la clase de estilo clásica, class "name":
__instance__
método __instance__
no existe.
Lo que esto significa es ser el más genérico que probablemente quiera usar, a menos que sepa que existe una instancia real.
El resultado de type()
es equivalente a obj.__class__
en las nuevas clases de estilo, y los objetos de clase no son seguros para comparar usando is
, use ==
lugar.
Para las nuevas clases de estilo, la forma preferible aquí sería type(obj) == Foo
.
Como Michael Hoffman señaló en su respuesta, aquí hay una diferencia entre las clases de estilo nuevas y antiguas, por lo que para el código compatible con versiones anteriores puede que necesite usar obj.__class__ == Foo
.
Para aquellos que afirman que isinstance(obj, Foo)
es preferible, considere la siguiente situación:
class Foo(object):
pass
class Bar(Foo):
pass
>>> obj = Bar()
>>> isinstance(obj, Foo)
True
>>> type(obj) == Foo
False
El OP quiere el comportamiento de type(obj) == Foo
, donde será falso aunque Foo
sea una clase base de Bar
.
Para las clases de estilo antiguo, hay una diferencia:
>>> class X: pass
...
>>> type(X)
<type ''classobj''>
>>> X.__class__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: class X has no attribute ''__class__''
>>> x = X()
>>> x.__class__
<class __main__.X at 0x171b5d50>
>>> type(x)
<type ''instance''>
El objetivo de las clases de estilo nuevo era unificar clase y tipo. Técnicamente hablando, __class__
es la única solución que funcionará tanto para instancias de clases nuevas como antiguas, pero también arrojará una excepción sobre los objetos de clase de estilo antiguo. Puede llamar a type()
en cualquier objeto, pero no todos los objetos tienen __class__
. Además, puede __class__
con __class__
de una manera que no puede ensuciar con type()
.
>>> class Z(object):
... def __getattribute__(self, name):
... return "ham"
...
>>> z = Z()
>>> z.__class__
''ham''
>>> type(z)
<class ''__main__.Z''>
Personalmente, normalmente tengo un entorno con clases de estilo nuevo solamente, y como una cuestión de estilo prefiero usar type()
ya que generalmente prefiero las funciones integradas cuando existen para usar atributos de magia. Por ejemplo, también preferiría bool(x)
a x.__nonzero__()
.
solo se debe usar para las comprobaciones de identidad, no para las comprobaciones de tipo (hay una excepción a la regla en la que puede y debe usar para verificar contra singletons).
Nota: en general, tampoco usaría type
y ==
para las verificaciones de tipo. La forma preferible para las verificaciones de tipo es isinstance(obj, Foo)
. Si alguna vez tienes una razón para verificar si algo no es una instancia de subclase, me huele a un diseño sospechoso. Cuando la class Foo(Bar):
entonces Bar
es un Foo
, y deberías evitar cualquier situación donde alguna parte de tu código tenga que funcionar en una instancia de Foo
, pero se rompa en una instancia de Bar
.