¿Cuál es la diferencia entre el estilo antiguo y las nuevas clases de estilo en Python? ¿Cuándo debo usar uno u otro?
¿Cuál es la diferencia entre el estilo antiguo y las nuevas clases de estilo en Python? ¿Cuándo debo usar uno u otro?
Respuestas:
De clases de estilo nuevo y clásico :
Hasta Python 2.1, las clases de estilo antiguo eran el único sabor disponible para el usuario.
El concepto de clase (estilo antiguo) no está relacionado con el concepto de tipo: si
x
es una instancia de una clase de estilo antiguo, entoncesx.__class__
designa la clase dex
, perotype(x)
siempre lo es<type 'instance'>
.Esto refleja el hecho de que todas las instancias de estilo antiguo, independientemente de su clase, se implementan con un único tipo incorporado, llamado instancia.
Se introdujeron nuevas clases de estilo en Python 2.2 para unificar los conceptos de clase y tipo . Una nueva clase de estilo es simplemente un tipo definido por el usuario, ni más ni menos.
Si x es una instancia de una clase de estilo nuevo,
type(x)
normalmente es lo mismo quex.__class__
(aunque esto no está garantizado: una instancia de clase de estilo nuevo puede anular el valor devuelto parax.__class__
).La principal motivación para introducir clases de estilo nuevo es proporcionar un modelo de objeto unificado con un metamodelo completo .
También tiene una serie de beneficios inmediatos, como la capacidad de subclasificar la mayoría de los tipos integrados, o la introducción de "descriptores", que permiten propiedades calculadas.
Por razones de compatibilidad, las clases siguen siendo antiguas por defecto .
Las clases de estilo nuevo se crean especificando otra clase de estilo nuevo (es decir, un tipo) como una clase principal o el objeto "tipo de nivel superior" si no se necesita otro padre.
El comportamiento de las clases de estilo nuevo difiere del de las clases de estilo antiguo en varios detalles importantes, además de qué tipo devuelve.
Algunos de estos cambios son fundamentales para el nuevo modelo de objetos, como la forma en que se invocan métodos especiales. Otros son "arreglos" que no se pudieron implementar antes por cuestiones de compatibilidad, como el orden de resolución del método en caso de herencia múltiple.
Python 3 solo tiene clases de estilo nuevo .
No importa si subclasa
object
o no, las clases son de nuevo estilo en Python 3.
super()
no funcionan en las clases de estilo antiguo. Sin mencionar, como dice ese artículo, hay soluciones fundamentales, como MRO, y métodos especiales, lo cual es más que una buena razón para usarlo.
Declaración sabia:
Las clases de estilo nuevo heredan del objeto o de otra clase de estilo nuevo.
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
Las clases de estilo antiguo no.
class OldStyleClass():
pass
Python 3 Nota:
Python 3 no admite clases de estilo antiguas, por lo que cualquiera de los formularios mencionados anteriormente da como resultado una clase de estilo nuevo.
object
.
class AnotherOldStyleClass: pass
class A: pass
y class A(): pass
son estrictamente equivalentes. El primero significa "A no hereda de ninguna clase principal" y el segundo significa "A no hereda de ninguna clase principal" . Eso es bastante similar not is
yis not
Cambios importantes de comportamiento entre las clases de estilo antiguas y nuevas.
Exception
(ejemplo a continuación)__slots__
adicionalSe mencionó en otras respuestas, pero aquí va un ejemplo concreto de la diferencia entre MRO clásico y MRO C3 (usado en nuevas clases de estilo).
La pregunta es el orden en que se buscan los atributos (que incluyen métodos y variables miembro) en herencia múltiple.
Las clases clásicas hacen una búsqueda profunda primero de izquierda a derecha. Detente en el primer partido. No tienen el __mro__
atributo.
class C: i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 0
assert C21().i == 2
try:
C12.__mro__
except AttributeError:
pass
else:
assert False
Las clases de estilo nuevo MRO son más complicadas de sintetizar en una sola oración en inglés. Se explica en detalle aquí . Una de sus propiedades es que solo se busca una clase base una vez que se han buscado todas sus clases derivadas. Tienen el __mro__
atributo que muestra el orden de búsqueda.
class C(object): i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 2
assert C21().i == 2
assert C12.__mro__ == (C12, C1, C2, C, object)
assert C21.__mro__ == (C21, C2, C1, C, object)
Exception
Alrededor de Python 2.5, muchas clases se podían generar, y alrededor de Python 2.6 se eliminó. En Python 2.7.3:
# OK, old:
class Old: pass
try:
raise Old()
except Old:
pass
else:
assert False
# TypeError, new not derived from `Exception`.
class New(object): pass
try:
raise New()
except TypeError:
pass
else:
assert False
# OK, derived from `Exception`.
class New(Exception): pass
try:
raise New()
except New:
pass
else:
assert False
# `'str'` is a new style object, so you can't raise it:
try:
raise 'str'
except TypeError:
pass
else:
assert False
Las clases de estilo antiguo siguen siendo marginalmente más rápidas para la búsqueda de atributos. Esto generalmente no es importante, pero puede ser útil en el código Python 2.x sensible al rendimiento:
En [3]: clase A: ...: def __init __ (self): ...: self.a = 'hola' ...: En [4]: clase B (objeto): ...: def __init __ (self): ...: self.a = 'hola' ...: En [6]: aobj = A () En [7]: bobj = B () En [8]:% timeit aobj.a 10000000 bucles, lo mejor de 3: 78.7 ns por bucle En [10]:% timeit bobj.a 10000000 bucles, lo mejor de 3: 86.9 ns por bucle
%timeit aobj.a
10000000 loops, best of 3: 66.1 ns per loop
%timeit bobj.a
10000000 loops, best of 3: 53.9 ns per loop
Guido ha escrito The Inside Story en New-Style Classes , un artículo realmente genial sobre la clase de estilo nuevo y antiguo en Python.
Python 3 solo tiene una clase de estilo nuevo. Incluso si escribe una 'clase de estilo antiguo', se deriva implícitamente de object
.
Las clases de estilo nuevo tienen algunas características avanzadas que faltan en las clases de estilo antiguo, como super
el nuevo C3 mro , algunos métodos mágicos, etc.
Aquí hay una muy práctica, verdadera / falsa diferencia. La única diferencia entre las dos versiones del siguiente código es que en la segunda versión, la Persona hereda del objeto . Aparte de eso, las dos versiones son idénticas, pero con resultados diferentes:
Clases de estilo antiguo
class Person():
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed1 is ahmed2
print ahmed1
print ahmed2
>>> False
<__main__.Person instance at 0xb74acf8c>
<__main__.Person instance at 0xb74ac6cc>
>>>
Clases de nuevo estilo
class Person(object):
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed2 is ahmed1
print ahmed1
print ahmed2
>>> True
<__main__.Person object at 0xb74ac66c>
<__main__.Person object at 0xb74ac66c>
>>>
_names_cache
es un diccionario que almacena en caché (almacena para la recuperación futura) cada nombre que le pase Person.__new__
. El método setdefault (definido en cualquier diccionario) toma dos argumentos: una clave y un valor. Si la clave está en el dict, devolverá su valor. Si no está en el dict, lo establecerá primero en el valor pasado como segundo argumento y luego lo devolverá.
__new__()
siempre se llama, y siempre construye un nuevo objeto, y luego lo arroja. En este caso if
es preferible a .setdefault()
.
__new__
en realidad no es algo para las clases de estilo antiguo, no se usa en la construcción de instancias (es solo un nombre aleatorio que se ve especial, como definir __spam__
). Por lo tanto, construir la clase de estilo antiguo solo invoca __init__
, mientras que la construcción de estilo nuevo invoca __new__
(fusionándose a instancia de singleton por nombre) para construir e __init__
inicializar.
Las clases de estilo nuevo heredan object
y deben escribirse como tales en Python 2.2 en adelante (es decir, en class Classname(object):
lugar de class Classname:
). El cambio principal es unificar tipos y clases, y el efecto secundario de esto es que le permite heredar de los tipos incorporados.
Lea la descripción para más detalles.
Las nuevas clases de estilo pueden usar super(Foo, self)
where Foo
es una clase y self
es la instancia.
super(type[, object-or-type])
Devuelve un objeto proxy que delega llamadas de método a una clase de tipo padre o hermano. Esto es útil para acceder a métodos heredados que se han anulado en una clase. El orden de búsqueda es el mismo que el utilizado por getattr (), excepto que se omite el tipo en sí.
Y en Python 3.x simplemente puede usar super()
dentro de una clase sin ningún parámetro.
type(x)
. Si no estoy subclasificando un tipo incorporado, entonces no parece haber ninguna ventaja que pueda ver de las clases de estilo nuevo. Hay una desventaja, que es la tipificación adicional de(object)
.