Nehmen wir an, ich habe eine einfache Klasse in Python
class Wharrgarbl(object):
def __init__(self, a, b, c, sum, version='old'):
self.a = a
self.b = b
self.c = c
self.sum = 6
self.version = version
def __int__(self):
return self.sum + 9000
def __what_goes_here__(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
Ich kann es sehr leicht in eine ganze Zahl umwandeln
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> int(w)
9006
Was toll ist! Aber jetzt möchte ich es auf ähnliche Weise zu einem Diktat machen
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}
Was muss ich definieren, damit dies funktioniert? Ich habe versucht , beide ersetzen __dict__
und dict
für __what_goes_here__
, sondern dict(w)
in einer Folge TypeError: Wharrgarbl object is not iterable
in beiden Fällen. Ich denke nicht, dass es das Problem lösen wird, wenn man die Klasse einfach iterierbar macht. Ich habe auch versucht, viele Brillen mit so vielen verschiedenen Formulierungen von "Python Cast Object to Dict" zu erstellen, wie ich mir vorstellen konnte, konnte aber nichts Relevantes finden: {
Ebenfalls! Beachten Sie, wie Aufruf w.__dict__
nicht tun , was ich will , denn es geht zu enthalten w.version
und w.sum
. Ich möchte die Besetzung auf dict
die gleiche Weise anpassen, wie ich die Besetzung int
mithilfe von anpassen kann def int(self)
.
Ich weiß, dass ich so etwas einfach machen kann
>>> w.__what_goes_here__()
{'a': 'one', 'c': 'three', 'b': 'two'}
Aber ich gehe davon aus, dass es einen pythonischen Weg gibt, um dict(w)
Arbeit zu machen , da es die gleiche Art von Dingen ist wie int(w)
oder str(w)
. Wenn es keinen pythonischeren Weg gibt, ist das auch in Ordnung, dachte ich nur, ich würde fragen. Oh! Ich denke, da es wichtig ist, ist dies für Python 2.7, aber auch Super-Bonuspunkte für eine 2.4 alte und kaputte Lösung.
Es gibt noch eine andere Frage: Überladen von __dict __ () in der Python-Klasse, die dieser ähnlich ist, aber möglicherweise so unterschiedlich ist, dass dies kein Duplikat ist. Ich glaube, dass OP fragt, wie alle Daten in seinen Klassenobjekten als Wörterbücher umgewandelt werden sollen. Ich suche nach einem individuelleren Ansatz, indem ich nicht möchte, dass alles im __dict__
Wörterbuch enthalten ist, das von zurückgegeben wird dict()
. Etwas wie öffentliche oder private Variablen kann ausreichen, um zu erklären, wonach ich suche. Die Objekte speichern einige Werte, die in Berechnungen verwendet werden und die ich nicht in den resultierenden Wörterbüchern anzeigen muss / möchte.
UPDATE: Ich habe mich für die asdict
vorgeschlagene Route entschieden, aber es war eine schwierige Entscheidung, die Antwort auf die Frage auszuwählen. Sowohl @RickTeachey als auch @ jpmc26 lieferten die Antwort, mit der ich rollen werde, aber der erstere hatte mehr Informationen und Optionen und landete ebenfalls auf dem gleichen Ergebnis und wurde mehr positiv bewertet, also ging ich mit. Überall Upvotes und danke für die Hilfe. Ich habe lange und hart am Stackoverflow gelauert und versuche, meine Zehen mehr ins Wasser zu bekommen.
Antworten:
Es gibt mindestens
fünfsechs Möglichkeiten. Der bevorzugte Weg hängt von Ihrem Anwendungsfall ab.Option 1: Fügen Sie einfach eine
asdict()
Methode hinzu.Basierend auf der Problembeschreibung würde ich sehr über die
asdict
Art und Weise nachdenken, wie andere Antworten vorgeschlagen werden. Dies liegt daran, dass Ihr Objekt anscheinend nicht wirklich eine Sammlung ist:class Wharrgarbl(object): ... def asdict(self): return {'a': self.a, 'b': self.b, 'c': self.c}
Die Verwendung der anderen unten aufgeführten Optionen kann für andere verwirrend sein, es sei denn, es ist sehr offensichtlich, welche Objektelemente als Schlüssel-Wert-Paare iteriert oder nicht iteriert oder angegeben werden.
Option 1a: Erben Sie Ihre Klasse von
'typing.NamedTuple'
(oder die meist gleichwertige'collections.namedtuple'
) und verwenden Sie die für Sie bereitgestellte_asdict
Methode .from typing import NamedTuple class Wharrgarbl(NamedTuple): a: str b: str c: str sum: int = 6 version: str = 'old'
Die Verwendung eines benannten Tupels ist eine sehr bequeme Möglichkeit, Ihrer Klasse mit minimalem Aufwand viele Funktionen hinzuzufügen, einschließlich einer
_asdict
Methode . Eine Einschränkung besteht jedoch darin, dass der NT, wie oben gezeigt, alle Mitglieder in seinen NT einbezieht_asdict
.Wenn es Mitglieder gibt, die Sie nicht in Ihr Wörterbuch aufnehmen möchten, müssen Sie das
_asdict
Ergebnis ändern :from typing import NamedTuple class Wharrgarbl(NamedTuple): a: str b: str c: str sum: int = 6 version: str = 'old' def _asdict(self): d = super()._asdict() del d['sum'] del d['version'] return d
Eine weitere Einschränkung besteht darin, dass NT schreibgeschützt ist. Dies kann wünschenswert sein oder nicht.
Option 2: Implementieren
__iter__
.So zum Beispiel:
def __iter__(self): yield 'a', self.a yield 'b', self.b yield 'c', self.c
Jetzt können Sie einfach tun:
Dies funktioniert, weil der
dict()
Konstruktor eine Iterable von(key, value)
Paaren akzeptiert , um ein Wörterbuch zu erstellen . Bevor Sie dies tun, stellen Sie sich die Frage, ob das Iterieren des Objekts als eine Reihe von Schlüssel-Wert-Paaren auf diese Weise - obwohl dies für die Erstellung eines geeignet istdict
- in anderen Kontexten tatsächlich überraschend sein kann. Stellen Sie sich beispielsweise die Frage "Wie solllist(my_object)
sich ... verhalten ?"Beachten Sie außerdem, dass der direkte Zugriff auf Werte mithilfe der
obj["a"]
Syntax get item nicht funktioniert und das Entpacken von Schlüsselwortargumenten nicht funktioniert. Für diese müssten Sie das Mapping-Protokoll implementieren.Option 3: Implementieren Sie das Zuordnungsprotokoll . Dies ermöglicht das Zugriffsverhalten nach Schlüssel, das
dict
ohne Verwendung in a umgewandelt wird__iter__
, und bietet auch das Entpackverhalten ({**my_obj}
) und das Entpackverhalten von Schlüsselwörtern, wenn alle Schlüssel Zeichenfolgen (dict(**my_obj)
) sind.Das Zuordnungsprotokoll erfordert, dass Sie (mindestens) zwei Methoden zusammen bereitstellen:
keys()
und__getitem__
.class MyKwargUnpackable: def keys(self): return list("abc") def __getitem__(self, key): return dict(zip("abc", "one two three".split()))[key]
Jetzt können Sie Dinge tun wie:
>>> m=MyKwargUnpackable() >>> m["a"] 'one' >>> dict(m) # cast to dict directly {'a': 'one', 'b': 'two', 'c': 'three'} >>> dict(**m) # unpack as kwargs {'a': 'one', 'b': 'two', 'c': 'three'}
Wie oben erwähnt, können Sie, wenn Sie eine ausreichend neue Python-Version verwenden, Ihr Mapping-Protokoll-Objekt auch in ein Wörterbuch-Verständnis wie dieses entpacken (und in diesem Fall ist es nicht erforderlich, dass Ihre Schlüssel Zeichenfolgen sind):
>>> {**m} {'a': 'one', 'b': 'two', 'c': 'three'}
Beachten Sie, dass das Zuordnungsprotokoll Vorrang vor der
__iter__
Methode hat, wenn ein Objektdict
direkt in ein Objekt umgewandelt wird (ohne kwarg-Entpacken zu verwenden, dhdict(m)
). Es ist also möglich - und manchmal auch bequem -, das Objekt anders verhalten zu lassen, wenn es als iterable (z. B.list(m)
) verwendet wird, als wenn es in adict
(dict(m)
) umgewandelt wird.HERVORZUHEBEN : Nur weil Sie das MappingProtokoll verwenden können, bedeutet nicht , dass Sie sollten dies tun . Ist es tatsächlich sinnvoll, Ihr Objekt als Satz von Schlüssel-Wert-Paaren oder als Schlüsselwortargumente und -werte weiterzugeben? Ist der Zugriff per Schlüssel - genau wie bei einem Wörterbuch - wirklich sinnvoll?
Wenn die Antwort auf diese Fragen Ja lautet , ist es wahrscheinlich eine gute Idee, die nächste Option in Betracht zu ziehen.
Option 4: Sehen Sie sich die Verwendung des
'collections.abc
Moduls an .Das Erben Ihrer Klasse von
'collections.abc.Mapping
oder das'collections.abc.MutableMapping
Signal an andere Benutzer, dass Ihre Klasse in jeder Hinsicht eine Zuordnung * ist und sich voraussichtlich so verhält.Sie können Ihr Objekt immer noch
dict
so bearbeiten, wie Sie es benötigen, aber es gibt wahrscheinlich wenig Grund, dies zu tun. Aufgrund der Typisierung von Entendict
wäre es die meiste Zeit nur ein zusätzlicher unnötiger Schritt , sich die Mühe zu machen, Ihr Mapping-Objekt in ein zu verwandeln.Diese Antwort könnte auch hilfreich sein.
Wie in den Kommentaren unten erwähnt: Es ist erwähnenswert, dass dies auf abc-Weise Ihre Objektklasse im Wesentlichen in eine
dict
ähnliche Klasse verwandelt (vorausgesetzt, Sie verwendenMutableMapping
und nicht die schreibgeschützteMapping
Basisklasse). Alles, was Sie tundict
könnten, könnten Sie mit Ihrem eigenen Klassenobjekt tun. Dies kann wünschenswert sein oder auch nicht.Beachten Sie auch die numerischen Abcs im
numbers
Modul:https://docs.python.org/3/library/numbers.html
Da Sie Ihr Objekt auch auf ein Objekt übertragen,
int
ist es möglicherweise sinnvoller, Ihre Klasse in eine vollwertige Klasse zu verwandeln,int
sodass das Casting nicht erforderlich ist.Option 5: Sehen Sie sich die Verwendung des
dataclasses
Moduls an (nur Python 3.7), das eine praktischeasdict()
Dienstprogrammmethode enthält.from dataclasses import dataclass, asdict, field, InitVar @dataclass class Wharrgarbl(object): a: int b: int c: int sum: InitVar[int] # note: InitVar will exclude this from the dict version: InitVar[str] = "old" def __post_init__(self, sum, version): self.sum = 6 # this looks like an OP mistake? self.version = str(version)
Jetzt können Sie dies tun:
>>> asdict(Wharrgarbl(1,2,3,4,"X")) {'a': 1, 'b': 2, 'c': 3}
Option 6: Verwenden Sie
typing.TypedDict
diese Option , die in Python 3.8 hinzugefügt wurde .HINWEIS: Option 6 ist wahrscheinlich NICHT das, wonach das OP oder andere Leser, die auf dem Titel dieser Frage basieren, suchen. Siehe zusätzliche Kommentare unten.
class Wharrgarbl(TypedDict): a: str b: str c: str
Bei Verwendung dieser Option ist das resultierende Objekt a
dict
(Hervorhebung: es ist kein aWharrgarbl
). Es gibt überhaupt keinen Grund, es auf ein Diktat zu "übertragen" (es sei denn, Sie machen eine Kopie).Und da das Objekt a ist
dict
, ist die Initialisierungssignatur mit der von identischdict
und akzeptiert als solche nur Schlüsselwortargumente oder ein anderes Wörterbuch.>>> w = Wharrgarbl(a=1,b=2,b=3) >>> w {'a': 1, 'b': 2, 'c': 3} >>> type(w) <class 'dict'>
Hervorgehoben : Die obige "Klasse"
Wharrgarbl
ist eigentlich überhaupt keine neue Klasse. Es ist einfach syntaktischer Zucker zum Erstellen typisierterdict
Objekte mit Feldern unterschiedlicher Typen für die Typprüfung.Daher kann diese Option sehr praktisch sein, um Lesern Ihres Codes (und auch einem Typprüfer wie mypy) zu signalisieren, dass von einem solchen
dict
Objekt bestimmte Schlüssel mit bestimmten Werttypen erwartet werden.Dies bedeutet jedoch, dass Sie beispielsweise keine anderen Methoden hinzufügen können, obwohl Sie Folgendes versuchen können:
class MyDict(TypedDict): def my_fancy_method(self): return "world changing result"
... aber es wird nicht funktionieren:
>>> MyDict().my_fancy_method() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'dict' object has no attribute 'my_fancy_method'
* "Mapping" ist zum Standard "Namen" des
dict
Ententyps gewordenquelle
for k, v in my_object
auch aktiviert wird.a
,b
undc
in dendict
. Dievars
Funktion gibt ein Wörterbuch mit ALLEN Objektelementen zurück, einschließlich derjenigen, die nicht gewünscht wurden:sum
undversion
. In einigen Fällenvars
wird es funktionieren, aber meiner Erfahrung nach ist es keine wirklich idiomatische Python. Normalerweise sind die Leute expliziter und sagen "Ich werfe jetzt mein Objekt in ein Wörterbuch", indem sie zobj.asdict()
. Mitvars
ist es normalerweise eher so,obj_kwargs = vars(obj)
gefolgt vonMyObject(**obj_kwargs)
. Mit anderen Worten:vars
wird meistens zum Kopieren von Objekten verwendet.self.__dict__
?Es gibt keine magische Methode, die macht, was Sie wollen. Die Antwort ist einfach, es angemessen zu benennen.
asdict
ist eine vernünftige Wahl für eine einfache Umstellung aufdict
, vor allem inspiriert vonnamedtuple
. Ihre Methode enthält jedoch offensichtlich eine spezielle Logik, die aus diesem Namen möglicherweise nicht sofort ersichtlich ist. Sie geben nur eine Teilmenge des Klassenstatus zurück. Umso besser, wenn Sie einen etwas ausführlicheren Namen finden, der die Konzepte klar kommuniziert.Andere Antworten schlagen die Verwendung vor
__iter__
, aber wenn Ihr Objekt nicht wirklich iterierbar ist (eine Reihe von Elementen darstellt), macht dies wirklich wenig Sinn und stellt einen unangenehmen Missbrauch der Methode dar. Die Tatsache, dass Sie einen Teil des Klassenzustands herausfiltern möchten, macht diesen Ansatz noch zweifelhafter.quelle
__iter__
wie andere Antworten vorgeschlagen haben: "Bei Zuordnungen sollte sie über die Schlüssel des Containers iterieren und auch als Methode iterkeys () verfügbar gemacht werden."Mapping
Basisklasse erweitern. Ein Objekt mit bestimmten benannten Attributen fällt nicht in diese Kategorie.so etwas würde wahrscheinlich funktionieren
class MyClass: def __init__(self,x,y,z): self.x = x self.y = y self.z = z def __iter__(self): #overridding this to return tuples of (key,value) return iter([('x',self.x),('y',self.y),('z',self.z)]) dict(MyClass(5,6,7)) # because dict knows how to deal with tuples of (key,value)
quelle
x
erstellt wurde, aber vor dem zweiten Tupel mity
. Bin ich falsch und tatsächlich ist das Listenverständnis in Bezug auf die Thread-Planung atomar?dis
Modul am besten, um die tatsächlichen Bytecodes anzuzeigen.Ich denke, das wird für dich funktionieren.
class A(object): def __init__(self, a, b, c, sum, version='old'): self.a = a self.b = b self.c = c self.sum = 6 self.version = version def __int__(self): return self.sum + 9000 def __iter__(self): return self.__dict__.iteritems() a = A(1,2,3,4,5) print dict(a)
Ausgabe
{'a': 1, 'c': 3, 'b': 2, 'sum': 6, 'version': 5}
quelle
__dict__
wie in der anderen vorgeschlagenen Antwort?self.__dict__
Elemente enthält, die ich in der Wörterbuchversion des Objekts nicht haben möchte.Wie viele andere würde ich vorschlagen, eine to_dict () -Funktion zu implementieren, anstatt (oder zusätzlich) das Casting in ein Wörterbuch zuzulassen. Ich denke, es macht es offensichtlicher, dass die Klasse diese Art von Funktionalität unterstützt. Sie können eine solche Methode leicht wie folgt implementieren:
def to_dict(self): class_vars = vars(MyClass) # get any "default" attrs defined at the class level inst_vars = vars(self) # get any attrs defined on the instance (self) all_vars = dict(class_vars) all_vars.update(inst_vars) # filter out private attributes public_vars = {k: v for k, v in all_vars.items() if not k.startswith('_')} return public_vars
quelle
Es ist schwer zu sagen, ohne den gesamten Kontext des Problems zu kennen, aber ich würde es nicht überschreiben
__iter__
.Ich würde
__what_goes_here__
auf der Klasse implementieren .as_dict(self: d = {...whatever you need...} return d
quelle
w.__dict__
das nicht das tut, was ich brauche.__dict__
könnte überschrieben werden, nicht wahr?Ich versuche eine Klasse zu schreiben, die "beides" ist
list
oder a istdict
. Ich möchte, dass der Programmierer in der Lage ist, dieses Objekt sowohl in einlist
( Casting der Schlüssel) als auch in ein "Cast" zu verwandelndict
(mit den Schlüsseln) .Betrachten Sie die Art und Weise, wie Python derzeit die
dict()
Besetzung ausführt: Es ruft aufMapping.update()
mit dem übergebenen Objekt auf. Dies ist der Code aus dem Python-Repo :def update(self, other=(), /, **kwds): ''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F. If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v ''' if isinstance(other, Mapping): for key in other: self[key] = other[key] elif hasattr(other, "keys"): for key in other.keys(): self[key] = other[key] else: for key, value in other: self[key] = value for key, value in kwds.items(): self[key] = value
Der letzte Unterfall der if-Anweisung, über
other
den sie iteriert, ist der, an den die meisten Menschen denken. Wie Sie sehen können, ist es jedoch auch möglich, eine zu habenkeys()
Eigenschaft . In Kombination mit a__getitem__()
sollte es einfach sein, eine Unterklasse ordnungsgemäß in ein Wörterbuch umzuwandeln:class Wharrgarbl(object): def __init__(self, a, b, c, sum, version='old'): self.a = a self.b = b self.c = c self.sum = 6 self.version = version def __int__(self): return self.sum + 9000 def __keys__(self): return ["a", "b", "c"] def __getitem__(self, key): # have obj["a"] -> obj.a return self.__getattribute__(key)
Dann wird das funktionieren:
>>> w = Wharrgarbl('one', 'two', 'three', 6) >>> dict(w) {'a': 'one', 'c': 'three', 'b': 'two'}
quelle
list
die Schlüssel__iter__
löschen möchten , können Sie eine Methode hinzufügen , die die Werte durchläuft, und das Castingdict
an funktioniert weiterhin. Beachten Sie, dass bei Wörterbüchern, die Sie in eine Liste umwandeln, normalerweise die SCHLÜSSEL zurückgegeben werden und nicht die gewünschten Werte. Dies könnte für andere Personen, die Ihren Code verwenden, überraschend sein, es sei denn, es ist offensichtlich, warum dies passieren würde.