Pregunta ¿Cuál es la diferencia entre el estilo antiguo y las nuevas clases de estilo en Python?


¿Cuál es la diferencia entre el estilo antiguo y las nuevas clases de estilo en Python? ¿Alguna vez hay una razón para usar clases antiguas en estos días?


855
2017-09-10 18:01


origen


Respuestas:


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 (antigua) no está relacionado con el concepto de tipo: si x es una instancia de una clase antigua, luego x.__class__ designa la clase de x, pero type(x) es siempre <type 'instance'>. Esto refleja el hecho de que todas las instancias antiguas, independientemente de su clase, se implementan con un único tipo de función, llamada 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 estilo nuevo, entonces type(x) es típicamente lo mismo que x.__class__ (aunque esto no está garantizado: se permite que una instancia de clase de estilo nuevo anule el valor devuelto para x.__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 incorporados, o la introducción de "descriptores", que permiten las propiedades calculadas.

Por razones de compatibilidad, las clases siguen siendo antiguas por defecto. Las clases de nuevo estilo se crean especificando otra clase de estilo nuevo (es decir, un tipo) como 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 antiguas en una serie de detalles importantes además del tipo que 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 podían 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 subclases de object o no, las clases son de estilo nuevo en Python 3.


487
2017-07-30 01:21



Declaración-sabio:

Las clases de nuevo estilo heredan del objeto o de otra clase de estilo nuevo.

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

Las clases antiguas no.

class OldStyleClass():
    pass

282
2017-11-13 09:36



Cambios de comportamiento importantes entre las clases de estilo antiguo y nuevo

  • súper adicional
  • MRO cambió (se explica a continuación)
  • descriptores adicional
  • los nuevos objetos de clase de estilo no se pueden generar a menos que se deriven de Exception (ejemplo abajo)
  • __slots__ adicional

MRO (Orden de resolución de método) cambiado

Se mencionó en otras respuestas, pero aquí va un ejemplo concreto de la diferencia entre el MRO clásico y C3 MRO (utilizado en las nuevas clases de estilo).

La pregunta es el orden en que los atributos (que incluyen métodos y variables de miembros) se buscan en herencia múltiple.

Clases clasicashacer una primera búsqueda de profundidad de izquierda a derecha. Parar en el primer partido. Ellos 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

Clases de nuevo estilo MRO es más complicado de sintetizar en una sola oración en inglés. Se explica en detalle aquí. Una de sus propiedades es que una clase base solo se busca una vez que han sido todas sus clases derivadas. Ellos 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)

Los nuevos objetos de clase de estilo no se pueden generar a menos que se deriven de Exception

Alrededor de Python 2.5 se pudieron generar 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

195
2017-07-12 11:26



Las clases de estilo antiguo son todavía 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:

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 loops, lo mejor de 3: 78.7 ns por ciclo

En [10]:% timeit bobj.a
10000000 loops, lo mejor de 3: 86.9 ns por ciclo

34
2018-04-24 13:41



Guido ha escrito La historia interna de las clases de nuevo estilo, un excelente artículo sobre la clase de estilo nuevo y antiguo en Python.

Python 3 tiene solo una clase de estilo nuevo, incluso si escribe una 'clase de estilo antiguo', se deriva implícitamente de object.

Las clases de nuevo estilo tienen algunas características avanzadas que carecen de clases antiguas, como super y el nuevo C3 mro, algunos métodos mágicos, etc.


30
2017-10-09 13:40



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 Person hereda de un 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>
>>>

17
2017-10-28 09:54



Las clases de nuevo estilo heredan de object y debe escribirse como tal en Python 2.2 en adelante (es decir class Classname(object): en lugar de class Classname:) El principal cambio es unificar tipos y clases, y el agradable efecto secundario de esto es que le permite heredar de los tipos incorporados.

Leer descrintro para más detalles.


7
2018-04-30 08:28