Was ist der Unterschied zwischen alten und neuen Stilklassen in Python? Wann sollte ich das eine oder andere verwenden?
Was ist der Unterschied zwischen alten und neuen Stilklassen in Python? Wann sollte ich das eine oder andere verwenden?
Antworten:
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
x
es sich um eine Instanz einer alten Klasse handelt,x.__class__
bezeichnet es die Klasse vonx
, ist es abertype(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 wiex.__class__
(obwohl dies nicht garantiert ist - eine Klasseninstanz neuen Stils darf den zurückgegebenen Wert überschreibenx.__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
object
oder nicht, sind Klassen in Python 3 neu.
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.
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.
object
.
class AnotherOldStyleClass: pass
class A: pass
und class A(): pass
sind 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 is
undis not
Wichtige Verhaltensänderungen zwischen alten und neuen Stilklassen
Exception
(Beispiel unten) abgeleitet wurden.__slots__
hinzugefügtEs 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)
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
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
%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 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. super
das neue C3 mro , einige magische Methoden usw.
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:
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>
>>>
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>
>>>
_names_cache
ist 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.
__new__()
immer aufgerufen wird, und es konstruiert immer ein neues Objekt und wirft es dann. In diesem Fall if
ist a dem vorzuziehen .setdefault()
.
__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.
Klassen neuen Stils erben von object
Python 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.
Neue Stilklassen können verwenden, super(Foo, self)
wo Foo
eine Klasse und self
die 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.
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)
.