Kann jemand namedtuple ändern oder eine alternative Klasse bereitstellen, damit sie für veränderbare Objekte funktioniert?
In erster Linie aus Gründen der Lesbarkeit möchte ich etwas Ähnliches wie namedtuple, das dies tut:
from Camelot import namedgroup
Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
Point(x=100, y=0)
Es muss möglich sein, das resultierende Objekt zu beizen. Und gemäß den Merkmalen des benannten Tupels muss die Reihenfolge der Ausgabe bei der Darstellung mit der Reihenfolge der Parameterliste beim Erstellen des Objekts übereinstimmen.
python
mutable
namedtuple
Alexander
quelle
quelle
namedtuple
s, es scheint , dass Sie keine Notwendigkeit, die Attribute von Index verweisen zu können, das heißt sop[0]
undp[1]
würde alternative Möglichkeiten zur Referenz seinx
undy
jeweils richtig?Antworten:
Es gibt eine veränderbare Alternative zu
collections.namedtuple
- recordclass .Es hat die gleiche API und den gleichen Speicherbedarf wie
namedtuple
und unterstützt Zuweisungen (es sollte auch schneller sein). Beispielsweise:Für Python 3.6 und höher
recordclass
(seit 0.5) werden folgende Schreibweisen unterstützt:Es gibt ein vollständigeres Beispiel (es enthält auch Leistungsvergleiche).
Seit 0.9
recordclass
bietet die Bibliothek eine weitere Variante - dierecordclass.structclass
Factory-Funktion. Es kann Klassen erzeugen, deren Instanzen weniger Speicher belegen als__slots__
Instanzen auf Basis. Dies kann für Instanzen mit Attributwerten wichtig sein, für die keine Referenzzyklen vorgesehen sind. Dies kann dazu beitragen, die Speichernutzung zu reduzieren, wenn Sie Millionen von Instanzen erstellen müssen. Hier ist ein anschauliches Beispiel .quelle
recordclass
ist langsamer, benötigt mehr Speicher und erfordert C-Erweiterungen im Vergleich zu Antti Haapalas Rezept undnamedlist
.recordclass
ist eine veränderbare Versioncollection.namedtuple
, die die API und den Speicherbedarf erbt, aber Zuweisungen unterstützt.namedlist
ist eigentlich eine Instanz der Python-Klasse mit Slots. Es ist nützlicher, wenn Sie keinen schnellen Zugriff auf die Felder per Index benötigen.recordclass
Beispiel (Python 3.5.2) ist ungefähr 2-3% langsamer als fürnamedlist
namedtuple
einfachen KlassenerstellungPoint = namedtuple('Point', 'x y')
kann Jedi Attribute automatisch vervollständigen, während dies nicht der Fall istrecordclass
. Wenn ich den längeren Erstellungscode (basierend aufRecordClass
) verwende, versteht Jedi diePoint
Klasse, aber nicht ihren Konstruktor oder ihre Attribute ... Gibt es eine Möglichkeit,recordclass
gut mit Jedi zusammenzuarbeiten?types.SimpleNamespace wurde in Python 3.3 eingeführt und unterstützt die angeforderten Anforderungen.
quelle
SimpleNamespace
scheitert an den Tests 6-10 (Zugriff per Index, iteratives Entpacken, Iteration, geordnetes Diktat, Ersetzen vor Ort) und 12, 13 (Felder, Slots). Beachten Sie, dass in der Dokumentation (die Sie in der Antwort verlinkt haben) ausdrücklich steht: "SimpleNamespace
Kann als Ersatz für nützlich seinclass NS: pass
. Verwenden Sienamedtuple()
stattdessen stattdessen einen strukturierten Datensatztyp ."SimpleNamespace
erstellt ebenfalls ein Objekt, keinen Klassenkonstruktor, und kann kein Ersatz für namedtuple sein. Der Typvergleich funktioniert nicht und der Speicherbedarf ist viel höher.Als sehr pythonische Alternative für diese Aufgabe können Sie seit Python-3.7 ein
dataclasses
Modul verwenden, das sich nicht nur wie eine veränderbare verhält,NamedTuple
da sie normale Klassendefinitionen verwenden, sondern auch andere Klassenfunktionen unterstützen.Aus PEP-0557:
Diese Funktion wird in PEP-0557 eingeführt. Weitere Informationen finden Sie unter dem bereitgestellten Dokumentationslink.
Beispiel:
Demo:
quelle
dataclass
Tests 6-10 (Zugriff per Index, iteratives Entpacken, Iteration, geordnetes Diktat, Ersetzen an Ort und Stelle) und 12, 13 (Felder, Slots) in Python 3.7 erforderlich ist und was nicht .1.Die neueste Namedlist 1.7 besteht alle Ihre Tests mit Python 2.7 und Python 3.5 ab dem 11. Januar 2016. Es handelt sich um eine reine Python-Implementierung, während
recordclass
es sich um eine C-Erweiterung handelt. Natürlich hängt es von Ihren Anforderungen ab, ob eine C-Erweiterung bevorzugt wird oder nicht.Ihre Tests (siehe aber auch den Hinweis unten):
Ausgabe unter Python 2.7
Der einzige Unterschied zu Python 3.5 besteht darin, dass die
namedlist
Größe kleiner geworden ist und 56 beträgt (Python 2.7 meldet 64).Beachten Sie, dass ich Ihren Test 10 für den direkten Austausch geändert habe. Das
namedlist
hat eine_replace()
Methode, die eine flache Kopie erstellt, und das ist für mich vollkommen sinnvoll, da sich dasnamedtuple
in der Standardbibliothek genauso verhält. Das Ändern der Semantik der_replace()
Methode wäre verwirrend. Meiner Meinung nach sollte die_update()
Methode für In-Place-Updates verwendet werden. Oder habe ich die Absicht Ihres Tests 10 nicht verstanden?quelle
namedlist
Speicherwerte in der Listeninstanz. Die Sache ist , dasscpython
‚slist
ist eigentlich ein dynamisches Array. Durch das Design wird mehr Speicher als erforderlich zugewiesen, um die Mutation der Liste billiger zu machen.list
und verwendet standardmäßig die__slots__
Optimierung. Als ich gemessen habe, war die Speichernutzung geringer alsrecordclass
: 96 Bytes gegenüber 104 Bytes für sechs Felder in Python 2.7recorclass
verwendet mehr Speicher, da es sich um eintuple
ähnliches Objekt mit variabler Speichergröße handelt.types.SimpleNamespace
. Leider mag es Pylint nicht :-(Die Antwort auf diese Frage scheint nein zu sein.
Unten ist ziemlich nah, aber es ist technisch nicht veränderlich. Dadurch wird eine neue
namedtuple()
Instanz mit einem aktualisierten x-Wert erstellt:Auf der anderen Seite können Sie eine einfache Klasse erstellen, mit der sich Klasseninstanzattribute
__slots__
häufig aktualisieren lassen:Um diese Antwort zu ergänzen, ist sie meiner Meinung nach
__slots__
hier nützlich, da sie speichereffizient ist, wenn Sie viele Klasseninstanzen erstellen. Der einzige Nachteil ist, dass Sie keine neuen Klassenattribute erstellen können.Hier ist ein relevanter Thread, der die Speichereffizienz veranschaulicht - Dictionary vs Object - der effizienter ist und warum?
Der zitierte Inhalt in der Antwort dieses Threads ist eine sehr prägnante Erklärung, warum
__slots__
speichereffizienter ist - Python-Slotsquelle
Im Folgenden ist eine gute Lösung für Python 3: Eine minimale Klasse mit
__slots__
undSequence
abstrakte Basisklasse; macht keine ausgefallene Fehlererkennung oder ähnliches, aber es funktioniert und verhält sich meistens wie ein veränderliches Tupel (außer Typecheck).Beispiel:
Wenn Sie möchten, können Sie auch eine Methode zum Erstellen der Klasse verwenden (obwohl die Verwendung einer expliziten Klasse transparenter ist):
Beispiel:
In Python 2 müssen Sie es leicht anpassen - wenn Sie von erben
Sequence
, hat die Klasse ein__dict__
und das__slots__
funktioniert nicht mehr.Die Lösung in Python 2 besteht darin, nicht von zu erben
Sequence
, sondernobject
. Fallsisinstance(Point, Sequence) == True
gewünscht, müssen Sie dieNamedMutableSequence
als Basisklasse registrieren, umSequence
:quelle
Lassen Sie uns dies mit dynamischer Typerstellung implementieren:
Dadurch werden die Attribute überprüft, um festzustellen, ob sie gültig sind, bevor der Vorgang fortgesetzt werden kann.
Also ist das pickleable? Ja, wenn (und nur wenn) Sie Folgendes tun:
Die Definition muss sich in Ihrem Namespace befinden und lange genug vorhanden sein, damit pickle sie finden kann. Wenn Sie dies so definieren, dass es in Ihrem Paket enthalten ist, sollte es funktionieren.
Pickle schlägt fehl, wenn Sie Folgendes tun oder die Definition temporär machen (z. B. außerhalb des Gültigkeitsbereichs, wenn die Funktion endet):
Und ja, die Reihenfolge der in der Typerstellung aufgeführten Felder bleibt erhalten.
quelle
__iter__
Methode mit hinzufügenfor k in self._attrs_: yield getattr(self, k)
, wird das Entpacken wie ein Tupel unterstützt.__len__
,__getitem__
und__setiem__
Methoden immer valus von Index zu unterstützen, wiep[0]
. Mit diesen letzten Bits scheint dies die vollständigste und korrekteste Antwort zu sein (für mich jedenfalls).__len__
und__iter__
sind gut.__getitem__
und__setitem__
kann wirklich zugeordnet werdenself.__dict__.__setitem__
undself.__dict__.__getitem__
Tupel sind per Definition unveränderlich.
Sie können jedoch eine Wörterbuchunterklasse erstellen, in der Sie mit Punktnotation auf die Attribute zugreifen können.
quelle
Wenn Sie ein ähnliches Verhalten wie namedtuples, aber veränderlich möchten, versuchen Sie es mit namedlist
Beachten Sie, dass es kein Tupel sein kann , um veränderlich zu sein .
quelle
Vorausgesetzt, die Leistung ist von geringer Bedeutung, könnte man einen dummen Hack verwenden wie:
quelle
z
, müssen Sie rufenmutable_z.z.pop(0)
dannmutable_z.z.append(new_value)
. Wenn Sie dies falsch verstehen, erhalten Sie mehr als ein Element und Ihr Programm verhält sich unerwartet.mutable_z.z[0] = newValue
. Es ist in der Tat ein Hack, wie gesagt.