512 Stimmen

Python-Wörterbuch aus den Feldern eines Objekts

Wissen Sie, ob es eine eingebaute Funktion gibt, um ein Wörterbuch aus einem beliebigen Objekt zu erstellen? Ich würde gerne so etwas wie dies tun:

>>> class Foo:
...     bar = 'hello'
...     baz = 'world'
...
>>> f = Foo()
>>> props(f)
{ 'bar' : 'hello', 'baz' : 'world' }

HINWEIS: Sie sollte keine Methoden enthalten. Nur Felder.

618voto

user6868 Punkte 7900

Beachten Sie, dass die beste Praxis in Python 2.7 ist, die neuartiges Klassen (nicht erforderlich bei Python 3), d.h.

class Foo(object):
   ...

Außerdem gibt es einen Unterschied zwischen einem "Objekt" und einer "Klasse". Um ein Wörterbuch aus einem willkürlichen Objekt ist es ausreichend, wenn Sie __dict__ . Normalerweise deklarieren Sie Ihre Methoden auf Klassenebene und Ihre Attribute auf Instanzebene, also __dict__ sollte in Ordnung sein. Zum Beispiel:

>>> class A(object):
...   def __init__(self):
...     self.b = 1
...     self.c = 2
...   def do_nothing(self):
...     pass
...
>>> a = A()
>>> a.__dict__
{'c': 2, 'b': 1}

Ein besserer Ansatz (vorgeschlagen von robert in Kommentaren) ist die eingebaute vars Funktion:

>>> vars(a)
{'c': 2, 'b': 1}

Alternativ, je nachdem, was Sie tun möchten, könnte es sinnvoll sein, von dict . Dann ist Ihre Klasse bereits ein Wörterbuch, und wenn Sie wollen, können Sie die getattr und/oder setattr durchrufen und das Diktat einstellen. Zum Beispiel:

class Foo(dict):
    def __init__(self):
        pass
    def __getattr__(self, attr):
        return self[attr]

    # etc...

4 Stimmen

Was passiert, wenn eines der Attribute von A einen benutzerdefinierten Getter hat? (eine Funktion mit einem @property-Dekorator)? Wird es trotzdem in ____dict____ angezeigt? Welchen Wert wird es haben?

15 Stimmen

__dict__ wird nicht funktionieren, wenn das Objekt Slots verwendet (oder in einem C-Modul definiert ist).

1 Stimmen

Gibt es eine Entsprechung dieser Methode für die Klassenobjekte? D.h. Anstatt f=Foo() zu verwenden und dann f.__dict__ auszuführen, direkt Foo.__dict__ ausführen?

251voto

Berislav Lopac Punkte 15229

Anstelle von x.__dict__ ist es tatsächlich pythonischer zu verwenden vars(x) .

7 Stimmen

Einverstanden. Beachten Sie, dass Sie auch in die andere Richtung konvertieren können (dict->class), indem Sie eingeben MyClass(**my_dict) vorausgesetzt, Sie haben einen Konstruktor mit Parametern definiert, die die Klassenattribute widerspiegeln. Es besteht keine Notwendigkeit, auf private Attribute zuzugreifen oder dict zu überschreiben.

3 Stimmen

Können Sie erklären, warum es pythonischer ist?

4 Stimmen

Erstens vermeidet Python im Allgemeinen den direkten Aufruf von dunder-Elementen, und es gibt fast immer eine Methode oder Funktion (oder einen Operator), um indirekt darauf zuzugreifen. Im Allgemeinen sind Dunder-Attribute und -Methoden ein Implementierungsdetail, und die Verwendung der "Wrapper"-Funktion ermöglicht es Ihnen, die beiden zu trennen. Zweitens können Sie auf diese Weise die vars Funktion und führen zusätzliche Funktionen ein, ohne das Objekt selbst zu verändern.

73voto

dF. Punkte 70587

En dir gibt Ihnen alle Attribute des Objekts, einschließlich spezieller Methoden wie __str__ , __dict__ und eine ganze Reihe anderer, die Sie wahrscheinlich nicht wollen. Aber Sie können etwas tun wie:

>>> class Foo(object):
...     bar = 'hello'
...     baz = 'world'
...
>>> f = Foo()
>>> [name for name in dir(f) if not name.startswith('__')]
[ 'bar', 'baz' ]
>>> dict((name, getattr(f, name)) for name in dir(f) if not name.startswith('__')) 
{ 'bar': 'hello', 'baz': 'world' }

Sie können dies also so erweitern, dass nur Datenattribute und keine Methoden zurückgegeben werden, indem Sie Ihre props Funktion wie folgt:

import inspect

def props(obj):
    pr = {}
    for name in dir(obj):
        value = getattr(obj, name)
        if not name.startswith('__') and not inspect.ismethod(value):
            pr[name] = value
    return pr

1 Stimmen

Dieser Code enthält Methoden. Gibt es eine Möglichkeit, Methoden auszuschließen? Ich brauche nur die Felder des Objekts. Danke

1 Stimmen

ismethod fängt keine Funktionen auf. Beispiel: inspect.ismethod(str.upper) . inspect.isfunction ist allerdings auch nicht viel hilfreicher. Ich bin mir nicht sicher, wie ich das am besten angehen soll.

0 Stimmen

Ich habe einige Optimierungen vorgenommen, um grob rekursiv und ignorieren alle Fehler zu einer Tiefe hier, danke! gist.github.com/thorsummoner/bf0142fd24974a0ced778768a33a306‌​9

31voto

Julio César Punkte 11682

Ich habe mich für eine Kombination aus beiden Antworten entschieden:

dict((key, value) for key, value in f.__dict__.iteritems() 
    if not callable(value) and not key.startswith('__'))

0 Stimmen

Das funktioniert auch, aber seien Sie sich bewusst, dass es nur die Attribute auf die Instanz, nicht auf die Klasse (wie Klasse Foo in Ihrem Beispiel) gesetzt wird...

1 Stimmen

Also, jcarrascal, Sie sind besser dran, wenn Sie den obigen Code in eine Funktion wie props() verpacken, dann können Sie entweder props(f) oder props(Foo) aufrufen. Beachten Sie, dass es fast immer besser ist, eine Funktion zu schreiben, als "Inline"-Code zu schreiben.

0 Stimmen

Nice, btw beachten Sie dies ist für Python2.7, für Python3 relpace iteritems() mit einfach Items().

24voto

Seaux Punkte 3339

Ich dachte, ich nehme mir etwas Zeit, um Ihnen zu zeigen, wie Sie ein Objekt in dict übersetzen können dict(obj) .

class A(object):
    d = '4'
    e = '5'
    f = '6'

    def __init__(self):
        self.a = '1'
        self.b = '2'
        self.c = '3'

    def __iter__(self):
        # first start by grabbing the Class items
        iters = dict((x,y) for x,y in A.__dict__.items() if x[:2] != '__')

        # then update the class items with the instance items
        iters.update(self.__dict__)

        # now 'yield' through the items
        for x,y in iters.items():
            yield x,y

a = A()
print(dict(a)) 
# prints "{'a': '1', 'c': '3', 'b': '2', 'e': '5', 'd': '4', 'f': '6'}"

Der wichtigste Abschnitt dieses Codes ist die __iter__ Funktion.

Wie in den Kommentaren erläutert, werden als erstes die Elemente der Klasse erfasst und alles, was mit "__" beginnt, verhindert.

Sobald Sie das erstellt haben dict dann können Sie die update dict-Funktion und übergeben Sie die Instanz __dict__ .

Damit erhalten Sie ein komplettes Klasse+Instanz-Wörterbuch der Mitglieder. Jetzt müssen Sie nur noch über sie iterieren und die Ergebnisse ausgeben.

Wenn Sie vorhaben, diese Funktion häufig zu verwenden, können Sie auch eine @iterable Klasse Dekorateur.

def iterable(cls):
    def iterfn(self):
        iters = dict((x,y) for x,y in cls.__dict__.items() if x[:2] != '__')
        iters.update(self.__dict__)

        for x,y in iters.items():
            yield x,y

    cls.__iter__ = iterfn
    return cls

@iterable
class B(object):
    d = 'd'
    e = 'e'
    f = 'f'

    def __init__(self):
        self.a = 'a'
        self.b = 'b'
        self.c = 'c'

b = B()
print(dict(b))

0 Stimmen

Damit werden auch alle Methoden erfasst, aber wir brauchen nur die Klasse und die Instanzfelder. Vielleicht dict((x, y) for x, y in KpiRow.__dict__.items() if x[:2] != '__' and not callable(y)) wird es lösen? Aber es könnte immer noch sein static Methoden :(

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X