Was ist der Unterschied zwischen alten und neuen Stilklassen in Python?

993

Was ist der Unterschied zwischen alten und neuen Stilklassen in Python? Wann sollte ich das eine oder andere verwenden?

Schreibgeschützt
quelle

Antworten:

560

Aus neuen und klassischen Klassen :

Bis zu Python 2.1 waren Klassen im alten Stil die einzige Variante, die dem Benutzer zur Verfügung stand.

Das Konzept der (alten) Klasse hat nichts mit dem Konzept des Typs zu tun: Wenn xes sich um eine Instanz einer alten Klasse handelt, x.__class__ bezeichnet es die Klasse von x, ist es aber type(x)immer <type 'instance'>.

Dies spiegelt die Tatsache wider, dass alle Instanzen alten Stils unabhängig von ihrer Klasse mit einem einzigen integrierten Typ implementiert werden, der als Instanz bezeichnet wird.

In Python 2.2 wurden Klassen neuen Stils eingeführt, um die Konzepte von Klasse und Typ zu vereinheitlichen . Eine Klasse neuen Stils ist einfach ein benutzerdefinierter Typ, nicht mehr und nicht weniger.

Wenn x eine Instanz einer Klasse neuen Stils ist, type(x)ist dies normalerweise dasselbe wie x.__class__(obwohl dies nicht garantiert ist - eine Klasseninstanz neuen Stils darf den zurückgegebenen Wert überschreiben x.__class__).

Die Hauptmotivation für die Einführung neuer Klassen besteht darin, ein einheitliches Objektmodell mit einem vollständigen Metamodell bereitzustellen .

Es hat auch eine Reihe von unmittelbaren Vorteilen, wie die Möglichkeit, die meisten integrierten Typen in Unterklassen zu unterteilen, oder die Einführung von "Deskriptoren", die berechnete Eigenschaften ermöglichen.

Aus Kompatibilitätsgründen sind Klassen standardmäßig immer noch im alten Stil .

Klassen neuen Stils werden erstellt, indem eine andere Klasse neuen Stils (dh ein Typ) als übergeordnete Klasse oder das Objekt "Typ der obersten Ebene" angegeben wird, wenn kein anderes übergeordnetes Objekt benötigt wird.

Das Verhalten von Klassen neuen Stils unterscheidet sich von dem von Klassen alten Stils in einer Reihe wichtiger Details, zusätzlich zu dem Typ, der zurückgegeben wird.

Einige dieser Änderungen sind für das neue Objektmodell von grundlegender Bedeutung, beispielsweise die Art und Weise, wie spezielle Methoden aufgerufen werden. Andere sind "Korrekturen", die aus Kompatibilitätsgründen zuvor nicht implementiert werden konnten, wie z. B. die Reihenfolge der Methodenauflösung bei Mehrfachvererbung.

Python 3 hat nur Klassen neuen Stils .

Unabhängig davon, ob Sie eine Unterklasse von haben objectoder nicht, sind Klassen in Python 3 neu.

Projesh Bhoumik
quelle
41
Keiner dieser Unterschiede klingt nach zwingenden Gründen für die Verwendung von Klassen im neuen Stil, aber jeder sagt, Sie sollten immer den neuen Stil verwenden. Wenn ich Enten tippe, wie ich sollte, muss ich nie verwenden type(x). Wenn ich einen eingebauten Typ nicht unterklassifiziere, scheint es keinen Vorteil zu geben, den ich von den Klassen neuen Stils sehen kann. Es gibt einen Nachteil, nämlich die zusätzliche Eingabe von (object).
rekursiv
78
Bestimmte Funktionen wie super()funktionieren nicht in Klassen alten Stils. Ganz zu schweigen davon, wie dieser Artikel sagt, dass es grundlegende Korrekturen wie MRO und spezielle Methoden gibt, was mehr als ein guter Grund ist, es zu verwenden.
John Doe
21
@User: Klassen im alten Stil verhalten sich in 2.7 genauso wie in 2.1 - und da sich nur wenige Menschen an die Macken erinnern und die Dokumentation die meisten von ihnen nicht mehr behandelt, sind sie noch schlimmer. Das obige Dokumentationszitat sagt dies direkt: Es gibt "Korrekturen", die in Klassen alten Stils nicht implementiert werden konnten. Verwenden Sie keine Klassen alten Stils, es sei denn, Sie möchten auf Macken stoßen, mit denen sich seit Python 2.1 niemand mehr befasst hat und die in der Dokumentation nicht mehr erklärt werden.
Abarnert
10
Hier ist ein Beispiel für eine Eigenart, auf die Sie möglicherweise stoßen, wenn Sie in 2.7 Klassen alten Stils verwenden: bugs.python.org/issue21785
KT.
5
Für alle, die sich fragen, ist ein guter Grund, explizit von Objekten in Python 3 zu erben, dass dies die Unterstützung mehrerer Versionen von Python erleichtert.
jpmc26
308

Deklarationstechnisch:

Klassen neuen Stils erben vom Objekt oder von einer anderen Klasse neuen Stils.

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

Old-Style-Klassen nicht.

class OldStyleClass():
    pass

Python 3 Hinweis:

Python 3 unterstützt keine Klassen im alten Stil, daher führt jede der oben genannten Formen zu einer Klasse im neuen Stil.

Mark Harrison
quelle
24
Wenn eine Klasse neuen Stils von einer anderen Klasse neuen Stils erbt, erbt sie per Erweiterung von object.
Aaronasterling
2
Ist dies ein falsches Beispiel für eine Python-Klasse im alten Stil? class AnotherOldStyleClass: pass
Ankur Agarwal
11
@abc Ich glaube das class A: passund class A(): passsind streng gleichwertig. Das erste bedeutet "A erbt keine übergeordnete Klasse" und das zweite bedeutet "A erbt keine übergeordnete Klasse" . Das ist ziemlich ähnlich zu not isundis not
eyquem
5
Nur als Randnotiz wird für 3.X automatisch die Vererbung von "Objekt" angenommen (was bedeutet, dass wir in 3.X keine Möglichkeit haben, "Objekt" nicht zu erben). Aus Gründen der Abwärtskompatibilität ist es jedoch nicht schlecht, "(Objekt)" dort zu belassen.
Yo Hsiao
1
Wenn wir technische Informationen zu geerbten Klassen erhalten möchten, sollten Sie in dieser Antwort beachten, dass Sie eine andere Klasse im alten Stil erstellen, indem Sie von einer Klasse im alten Stil erben. (Wie geschrieben, lässt diese Antwort den Benutzer fragen, ob Sie von einer alten Klasse erben können. Sie können.)
jpmc26
224

Wichtige Verhaltensänderungen zwischen alten und neuen Stilklassen

  • super hinzugefügt
  • MRO geändert (unten erklärt)
  • Deskriptoren hinzugefügt
  • Neue Stilklassenobjekte können nur ausgelöst werden, wenn sie von Exception(Beispiel unten) abgeleitet wurden.
  • __slots__ hinzugefügt

MRO (Method Resolution Order) geändert

Es wurde in anderen Antworten erwähnt, aber hier ist ein konkretes Beispiel für den Unterschied zwischen klassischem MRO und C3-MRO (in neuen Stilklassen verwendet).

Die Frage ist die Reihenfolge, in der Attribute (einschließlich Methoden und Elementvariablen) in Mehrfachvererbung gesucht werden.

Klassische Klassen führen eine Tiefensuche von links nach rechts durch. Stoppen Sie beim ersten Spiel. Sie haben das __mro__Attribut nicht.

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

Neue Klassen MRO ist komplizierter in einem einzigen englischen Satz zu synthetisieren. Es wird hier ausführlich erklärt . Eine seiner Eigenschaften ist, dass eine Basisklasse erst gesucht wird, wenn alle abgeleiteten Klassen vorhanden sind. Sie haben das __mro__Attribut, das die Suchreihenfolge anzeigt.

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)

Neue Stilklassenobjekte können nur ausgelöst werden, wenn sie von abgeleitet sind Exception

Um Python 2.5 konnten viele Klassen erhöht werden, und um Python 2.6 wurde dies entfernt. Unter 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
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
quelle
8
Schöne klare Zusammenfassung, danke. Wenn Sie "schwer auf Englisch zu erklären" sagen, beschreiben Sie wahrscheinlich eine Nachbestellungs-Tiefensuche im Gegensatz zu der alten Klasse, die eine Vorbestellungs-Tiefensuche verwendet. (Vorbestellung bedeutet, dass wir uns vor unserem ersten Kind suchen, und Nachbestellung bedeutet, dass wir uns nach unserem letzten Kind suchen).
Steve Carter
40

Alte Stilklassen sind für die Attributsuche immer noch geringfügig schneller. Dies ist normalerweise nicht wichtig, kann jedoch in leistungsempfindlichem Python 2.x-Code hilfreich sein:

In [3]: Klasse A:
   ...: def __init __ (self):
   ...: self.a = 'Hallo zusammen'
   ...:

In [4]: ​​Klasse B (Objekt):
   ...: def __init __ (self):
   ...: self.a = 'Hallo zusammen'
   ...:

In [6]: aobj = A ()
In [7]: bobj = B ()

In [8]:% timeit aobj.a
10000000 Schleifen, am besten 3: 78,7 ns pro Schleife

In [10]:% timeit bobj.a
10000000 Schleifen, am besten 3: 86,9 ns pro Schleife
Xioxox
quelle
5
Interessant, dass Sie in der Praxis bemerkt haben, ich habe gerade gelesen, dass dies daran liegt, dass Klassen neuen Stils, sobald sie das Attribut im Instanzdikt gefunden haben, eine zusätzliche Suche durchführen müssen, um herauszufinden, ob es sich um eine Beschreibung handelt, dh um eine Beschreibung get- Methode, die aufgerufen werden muss, um den zurückzugebenden Wert zu erhalten. Alte Stilklassen geben das gefundene Objekt einfach ohne zusätzliche Berechnungen zurück (unterstützen dann aber keine Deskriptoren). Sie können mehr in diesem ausgezeichneten Beitrag von Guido python-history.blogspot.co.uk/2010/06/… lesen , insbesondere den Abschnitt über Slots
xuloChavez
1
scheint mit CPython 2.7.2 nicht wahr zu sein:%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
Benedikt Waldvogel
1
Noch schneller für aobj in CPython 2.7.2 unter x86-64 Linux für mich.
Xioxox
41
Es ist wahrscheinlich eine schlechte Idee, sich für leistungsempfindliche Anwendungen auf reinen Python-Code zu verlassen. Niemand sagt: "Ich brauche schnellen Code, also verwende ich Python-Klassen im alten Stil." Numpy zählt nicht als reines Python.
Phillip Cloud
Auch in IPython 2.7.6 ist dies nicht der Fall. '' '' 477 ns vs. 456 ns pro Schleife '' ''
kmonsoor
37

Guido hat The Inside Story über Klassen im neuen Stil geschrieben , einen wirklich großartigen Artikel über Klassen im neuen und alten Stil in Python.

Python 3 hat nur eine neue Klasse. Selbst wenn Sie eine Klasse im alten Stil schreiben, wird sie implizit von abgeleitet object.

Klassen im neuen Stil haben einige erweiterte Funktionen, die in Klassen im alten Stil fehlen, wie z. B. superdas neue C3 mro , einige magische Methoden usw.

Xiao Hanyu
quelle
24

Hier ist ein sehr praktischer, wahr / falsch Unterschied. Der einzige Unterschied zwischen den beiden Versionen des folgenden Codes besteht darin, dass in der zweiten Version Person vom Objekt erbt . Davon abgesehen sind die beiden Versionen identisch, jedoch mit unterschiedlichen Ergebnissen:

  1. Klassen im alten Stil

    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. Klassen neuen Stils

    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>
    >>>
ychaouche
quelle
2
Was macht '_names_cache'? Könnten Sie eine Referenz teilen?
Muatik
4
_names_cacheist ein Wörterbuch, das jeden Namen, an den Sie übergeben, zwischenspeichert (zum späteren Abrufen speichert) Person.__new__. Die setdefault-Methode (in einem beliebigen Wörterbuch definiert) verwendet zwei Argumente: einen Schlüssel und einen Wert. Befindet sich der Schlüssel im Diktat, wird sein Wert zurückgegeben. Wenn es nicht im Diktat enthalten ist, wird es zuerst auf den als zweites Argument übergebenen Wert gesetzt und dann zurückgegeben.
Ychaouche
4
Die Verwendung ist falsch. Die Idee ist, kein neues Objekt zu konstruieren, wenn es bereits existiert, sondern in Ihrem Fall __new__()immer aufgerufen wird, und es konstruiert immer ein neues Objekt und wirft es dann. In diesem Fall ifist a dem vorzuziehen .setdefault().
Amit Upadhyay
Aber ich habe nicht verstanden, warum der Unterschied in der Ausgabe ist, dh in der alten Stilklasse waren die beiden Instanzen unterschiedlich, daher wurde False zurückgegeben, aber in der neuen Stilklasse sind beide Instanzen gleich. Wie ? Was ist die Änderung in der neuen Stilklasse, die die beiden Instanzen gleich gemacht hat, die nicht in der alten Stilklasse waren?
Pabitra Pati
1
@PabitraPati: Es ist eine Art billige Demonstration hier. __new__ist eigentlich keine Sache für Klassen alten Stils, es wird nicht in der Instanzkonstruktion verwendet (es ist nur ein zufälliger Name, der besonders aussieht, wie das Definieren __spam__). Das Konstruieren der Klasse im alten Stil ruft also nur auf __init__, während die Konstruktion im neuen Stil __new__(Zusammenführen zu einer Singleton-Instanz nach Namen) aufruft __init__, um sie zu konstruieren und zu initialisieren.
ShadowRanger
10

Klassen neuen Stils erben von objectPython 2.2 und müssen als solche geschrieben werden (dh class Classname(object):anstelle von class Classname:). Die wichtigste Änderung besteht darin, Typen und Klassen zu vereinheitlichen. Der nette Nebeneffekt besteht darin, dass Sie von integrierten Typen erben können.

Lesen Sie die Beschreibung für weitere Details.

Heiligenschein
quelle
8

Neue Stilklassen können verwenden, super(Foo, self)wo Fooeine Klasse und selfdie Instanz ist.

super(type[, object-or-type])

Gibt ein Proxy-Objekt zurück, das Methodenaufrufe an eine übergeordnete oder Geschwisterklasse vom Typ delegiert. Dies ist nützlich, um auf geerbte Methoden zuzugreifen, die in einer Klasse überschrieben wurden. Die Suchreihenfolge ist dieselbe wie die von getattr () verwendete, außer dass der Typ selbst übersprungen wird.

Und in Python 3.x können Sie einfach super()innerhalb einer Klasse ohne Parameter verwenden.

Jamylak
quelle