proposal - ¿Cuál es la diferencia entre las clases de estilo antiguo y nuevo en Python?
python enhancement proposal no 8 pep8 (9)
¿Cuál es la diferencia entre las clases de estilo antiguo y nuevo en Python? ¿Cuándo debo usar uno o el otro?
Aquí hay una diferencia muy práctica, Verdadero / Falso. 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:
1) 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>
>>>
2) 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>
>>>
De http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes :
Hasta Python 2.1, las clases antiguas eran el único sabor disponible para el usuario.
El concepto de clase (de 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
, pero eltype(x)
siempre 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 integrado, llamado instancia.
Las clases de nuevo estilo se introdujeron en Python 2.2 para unificar los conceptos de clase y tipo . Una clase de nuevo estilo es simplemente un tipo definido por el usuario, ni más ni menos.
Si x es una instancia de una clase de nuevo estilo, entonces el
type(x)
suele ser el mismo quex.__class__
(aunque esto no está garantizado, se permite una instancia de clase de nuevo estilo para anular el valor devuelto parax.__class__
) .La motivación principal para introducir clases de nuevo estilo 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 incorporados, o la introducción de "descriptores", que permiten las propiedades computadas.
Por razones de compatibilidad, las clases siguen siendo de estilo antiguo por defecto .
Las clases de nuevo estilo se crean especificando otra clase de nuevo estilo (es decir, un tipo) como clase principal, o el objeto "tipo de nivel superior" si no se necesita ningún otro padre.
El comportamiento de las clases de nuevo estilo difiere del de las clases de estilo antiguo en una serie de 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 los métodos especiales. Otros son "arreglos" que no se pudieron implementar antes por problemas de compatibilidad, como el orden de resolución del método en caso de herencia múltiple.
Python 3 solo tiene clases de nuevo estilo .
No importa si subclasifica de
object
o no, las clases son de estilo nuevo en Python 3.
Guido ha escrito The Inside Story en New-Style Classes , un excelente artículo sobre el estilo nuevo y la clase de estilo antiguo en Python.
Python 3 solo tiene una clase de estilo nuevo, incluso si escribe una ''clase de estilo antiguo'', se deriva implícitamente del object
.
Las clases de nuevo estilo tienen algunas características avanzadas que faltan en las clases de estilo antiguo, como super
y el nuevo C3 Mro , algunos métodos mágicos, etc.
Las clases de estilo antiguo aún son ligeramente más rápidas para la búsqueda de atributos. Esto no suele ser importante, pero puede ser útil en el código de Python 2.x sensible al rendimiento:
In [3]: class A: ...: def __init__(self): ...: self.a = ''hi there'' ...: In [4]: class B(object): ...: def __init__(self): ...: self.a = ''hi there'' ...: In [6]: aobj = A() In [7]: bobj = B() In [8]: %timeit aobj.a 10000000 loops, best of 3: 78.7 ns per loop In [10]: %timeit bobj.a 10000000 loops, best of 3: 86.9 ns per loop
Las clases de estilo nuevo se heredan del object
y deben escribirse como tales en Python 2.2 en adelante (es decir, class Classname(object):
lugar de class Classname:
. El cambio principal es unificar tipos y clases, y el efecto colateral de esto es que le permite heredar de los tipos incorporados.
Lea descrintro para más detalles.
Las nuevas clases de estilo pueden usar super(Foo, self)
donde Foo
es una clase y self
es la instancia.
super(type[, object-or-type])
Devuelva un objeto proxy que delegue las llamadas de método a una clase de tipo padre o hermana. Esto es útil para acceder a los 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 propio tipo.
Y en Python 3.x puedes simplemente usar super()
dentro de una clase sin parámetros.
O más bien, siempre debe usar clases de nuevo estilo, a menos que tenga un código que necesite trabajar con versiones de Python anteriores a 2.2.
Cambios importantes de comportamiento entre las clases de estilo antiguo y nuevo.
- super agregado
- MRO cambiado (explicado abajo)
- descriptors añadidos
- los nuevos objetos de clase de estilo no se pueden generar a menos que se deriven de
Exception
(ejemplo a continuación) -
__slots__
agregado
MRO (Orden de resolución de métodos) cambiado
Se mencionó en otras respuestas, pero aquí hay un ejemplo concreto de la diferencia entre el MRO clásico y el C3 MRO (usado en las nuevas clases de estilo).
La pregunta es el orden en el que los atributos (que incluyen métodos y variables de miembro) se buscan en herencia múltiple.
Las clases clásicas hacen una primera búsqueda profunda de izquierda a derecha. Pare en el primer partido. No tienen el atributo __mro__
.
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 nuevo estilo MRO son más complicadas de sintetizar en una sola oración en inglés. Se explica en detalle here . Una de sus propiedades es que una clase base solo se busca una vez que todas sus clases derivadas han sido buscadas. Tienen el atributo __mro__
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)
Los nuevos objetos de clase de estilo no se pueden levantar a menos que se deriven de la Exception
Alrededor de Python 2.5 se podían elevar muchas clases, alrededor de Python 2.6 esto 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
Declaración en cuanto a:
Las clases de nuevo estilo se heredan de un objeto o de otra clase de nuevo estilo.
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
Las clases antiguas no lo hacen.
class OldStyleClass():
pass