web-dev-qa-db-de.com

Wie verwende ich einen Punkt "." auf Mitglieder des Wörterbuchs zugreifen?

Wie mache ich Mitglieder des Python-Wörterbuchs über einen Punkt "."

Anstatt mydict['val'] zu schreiben, möchte ich mydict.val schreiben.

Ich möchte auch auf diese Weise auf verschachtelte Dicts zugreifen. Zum Beispiel

mydict.mydict2.val 

würde sich beziehen 

mydict = { 'mydict2': { 'val': ... } }
177
bodacydo

Sie können es mit dieser Klasse tun, die ich gerade gemacht habe. Mit dieser Klasse können Sie das Map-Objekt wie ein anderes Wörterbuch (einschließlich Json-Serialisierung) oder mit der Punktnotation verwenden. Ich hoffe Dir zu helfen:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Verwendungsbeispiele:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']
101
epool

Ich habe dies immer in einer util-Datei aufbewahrt. Sie können es auch als Mixin für Ihre eigenen Klassen verwenden.

class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'

mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'
159
derek73

dotmap über pip installieren

pip install dotmap

Es macht alles, was Sie wollen, und Unterklassen dict. Es funktioniert also wie ein normales Wörterbuch:

from dotmap import DotMap

m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'

Darüber hinaus können Sie es in und aus dict-Objekten konvertieren:

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

Das heißt, wenn sich etwas, auf das Sie zugreifen möchten, bereits in der Variable dict befindet, können Sie es für einen einfachen Zugriff in eine DotMap umwandeln:

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

Schließlich werden automatisch neue untergeordnete DotMap-Instanzen erstellt, sodass Sie Folgendes tun können:

m = DotMap()
m.people.steve.age = 31

Vergleich mit Bündel

Vollständige Offenlegung: Ich bin der Schöpfer der DotMap . Ich habe es erstellt, weil Bunch diese Funktionen fehlte

  • erinnern an die Bestellpositionen werden hinzugefügt und in dieser Reihenfolge durchlaufen
  • automatische untergeordnete DotMap-Erstellung, die Zeit spart und für saubereren Code sorgt, wenn Sie über viele Hierarchien verfügen
  • konstruieren aus einer dict und rekursives Konvertieren aller untergeordneten dict-Instanzen in DotMap
81
Chris Redford

Leiten Sie von dict ab und implementieren Sie __getattr__ und __setattr__.

Oder Sie können Bunch verwenden, was sehr ähnlich ist.

Ich glaube nicht, dass es möglich ist, die integrierte Diktorklasse zu überwachen.

56
Kugel

Ich habe das versucht:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

sie können auch __getattribute__ versuchen.

jedes Diktat zu einer Art Punktdict machen, wäre gut genug. Wenn Sie dies von einem mehrschichtigen Diktat aus initiieren möchten, versuchen Sie auch, __init__ zu implementieren.

16
tdihp

Fabric hat eine wirklich schöne, minimale Implementierung . Um den verschachtelten Zugriff zu ermöglichen, können wir eine defaultdict verwenden, und das Ergebnis sieht ungefähr so ​​aus:

from collections import defaultdict

class AttributeDict(defaultdict):
    def __init__(self):
        super(AttributeDict, self).__init__(AttributeDict)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(key)

    def __setattr__(self, key, value):
        self[key] = value

Nutzen Sie es wie folgt:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

Das geht ein wenig auf Kugel's Antwort von "Ableiten von und Ablegen von __getattr__ und __setattr__" ein. Jetzt weißt du wie!

12
Dave

Wenn Sie Ihr modifiziertes Wörterbuch auswählen möchten, müssen Sie den obigen Antworten einige Statusmethoden hinzufügen:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self
8
volodymyr

Nicht Der Zugriff auf Attribute und die Indizierung sind in Python verschiedene Dinge, und Sie sollten nicht möchten, dass sie dasselbe tun. Erstellen Sie eine Klasse (möglicherweise eine von namedtuple erstellte), wenn Sie über etwas verfügen, das über zugreifbare Attribute verfügen soll, und verwenden Sie die []-Notation, um ein Element aus einem Diktat abzurufen.

6
Mike Graham

Aufbauend auf Kugel Antwort und unter Berücksichtigung von Mike Grahams Vorsicht, was ist, wenn wir einen Wrapper machen? 

class DictWrap(object):
  """ Wrap an existing dict, or create a new one, and access with either dot 
    notation or key lookup.

    The attribute _data is reserved and stores the underlying dictionary.
    When using the += operator with create=True, the empty nested dict is 
    replaced with the operand, effectively creating a default dictionary
    of mixed types.

    args:
      d({}): Existing dict to wrap, an empty dict is created by default
      create(True): Create an empty, nested dict instead of raising a KeyError

    example:
      >>>dw = DictWrap({'pp':3})
      >>>dw.a.b += 2
      >>>dw.a.b += 2
      >>>dw.a['c'] += 'Hello'
      >>>dw.a['c'] += ' World'
      >>>dw.a.d
      >>>print dw._data
      {'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}

  """

  def __init__(self, d=None, create=True):
    if d is None:
      d = {}
    supr = super(DictWrap, self)  
    supr.__setattr__('_data', d)
    supr.__setattr__('__create', create)

  def __getattr__(self, name):
    try:
      value = self._data[name]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[name] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setattr__(self, name, value):
    self._data[name] = value  

  def __getitem__(self, key):
    try:
      value = self._data[key]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[key] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setitem__(self, key, value):
    self._data[key] = value

  def __iadd__(self, other):
    if self._data:
      raise TypeError("A Nested dict will only be replaced if it's empty")
    else:
      return other
6

Die Sprache selbst unterstützt dies nicht, aber manchmal ist dies immer noch eine nützliche Voraussetzung. Neben dem Bunch-Rezept können Sie auch eine kleine Methode schreiben, die mithilfe eines gepunkteten Strings auf ein Wörterbuch zugreifen kann:

def get_var(input_dict, accessor_string):
    """Gets data from a dictionary using a dotted accessor-string"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

was würde so etwas unterstützen:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>
4
pbanka

Ich mag das Munch und es gibt viele praktische Optionen über den Punktzugang. 

munch importieren 

temp_1 = {'person': {'fname': 'senthil', 'lname': 'ramalingam'}}

dict_munch = munch.munchify (temp_1)

dict_munch.person.fname

4
Senthil

Um auf die Antwort von epool aufzubauen, können Sie mit dieser Version über den Punktoperator auf alle Diktate zugreifen:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

Zum Beispiel gibt foo.bar.baz[1].baba"loo" zurück.

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    if isinstance(v, dict):
                        v = Map(v)
                    if isinstance(v, list):
                        self.__convert(v)
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                if isinstance(v, dict):
                    v = Map(v)
                Elif isinstance(v, list):
                    self.__convert(v)
                self[k] = v

    def __convert(self, v):
        for elem in xrange(0, len(v)):
            if isinstance(v[elem], dict):
                v[elem] = Map(v[elem])
            Elif isinstance(v[elem], list):
                self.__convert(v[elem])

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]
3
user4343502
def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

    class Struct:
        def __init__(self, **entries):
            self.__dict__.update(entries)

    return Struct(**dick)

Wenn Sie sich entscheiden, diese dict dauerhaft in ein Objekt umzuwandeln, sollte dies geschehen. Sie können ein Wegwerfobjekt direkt vor dem Zugriff erstellen. 

d = dict_to_object(d)
3
nehemiah

Verwenden Sie __getattr__, sehr einfach, funktioniert in Python 3.4.3

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

Ausgabe:

10000
StackOverflow
2
IRSHAD

Am Ende habe ich BEIDE die AttrDict - und die Bunch - Bibliotheken ausprobiert und fand sie für meinen Gebrauch viel zu langsam. Nachdem ein Freund und ich ihn untersucht hatten, stellten wir fest, dass die Hauptmethode zum Schreiben dieser Bibliotheken darin liegt, dass die Bibliothek aggressiv durch ein verschachteltes Objekt rekursiert und Kopien des Wörterbuchobjekts erstellt. Vor diesem Hintergrund haben wir zwei wichtige Änderungen vorgenommen. 1) Wir haben Attribute faul geladen 2) Anstatt Kopien eines Wörterbuchobjekts zu erstellen, erstellen wir Kopien eines leichtgewichtigen Proxy-Objekts. Dies ist die endgültige Implementierung. Die Leistungssteigerung bei der Verwendung dieses Codes ist unglaublich. Wenn Sie AttrDict oder Bunch verwenden, verbrauchen diese beiden Bibliotheken alleine 1/2 bzw. 1/3 meiner Anforderungszeit (was !?). Dieser Code reduzierte diese Zeit auf fast nichts (irgendwo im Bereich von 0,5 ms). Dies hängt natürlich von Ihren Anforderungen ab. Wenn Sie jedoch diese Funktionalität in Ihrem Code verwenden, sollten Sie auf jeden Fall etwas so einfaches verwenden. 

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    def __getattr__(self, key):
        try:
            return wrap(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)

    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__

class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    # you probably also want to proxy important list properties along like
    # __iter__ and __len__

def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (Tuple, list)):
        return ListProxy(value)
    return value

Siehe die ursprüngliche Implementierung here by https://stackoverflow.com/users/704327/michael-merickel .

Die andere Sache ist, dass diese Implementierung ziemlich einfach ist und nicht alle Methoden implementiert, die Sie benötigen. Sie müssen diese nach Bedarf in die DictProxy- oder ListProxy-Objekte schreiben.

1
JayD3e

Eine einfache Möglichkeit, einen Punktzugriff (aber keinen Arrayzugriff) zu erhalten, ist die Verwendung eines einfachen Objekts in Python. So was:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

... und verwende es so:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

... um es in ein Diktat umzuwandeln:

>>> print(obj.__dict__)
{"key": "value"}
0
Emil Stenström

Ich möchte meine eigene Lösung in den Ring werfen:

https://github.com/skorokithakis/jsane

Damit können Sie JSON in etwas analysieren, auf das Sie auf with.attribute.lookups.like.this.r() zugreifen können, hauptsächlich, weil ich diese Antwort nicht gesehen habe, bevor Sie mit der Arbeit begonnen haben.

Dies funktioniert auch mit verschachtelten Dikten und stellt sicher, dass die später angehängten Dikte dasselbe Verhalten haben:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__
0
Yaniv K.

Diese Lösung ist eine Verfeinerung der von epool angebotenen, um die Anforderung des OP an konsistente Zugriffe auf verschachtelte Diktiere zu erfüllen. Die Lösung von epool erlaubte keinen Zugriff auf verschachtelte Diktate.

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(YAMLobj, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(YAMLobj, self).__delitem__(key)
        del self.__dict__[key]

Mit dieser Klasse kann man nun etwa Folgendes tun: A.B.C.D

0
deepak

Eine etwas heikle Lösung

class DotDict(dict):

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):

        def typer(candidate):
            if isinstance(candidate, dict):
                return DotDict(candidate)

            if isinstance(candidate, str):  # iterable but no need to iter
                return candidate

            try:  # other iterable are processed as list
                return [typer(item) for item in candidate]
            except TypeError:
                return candidate

            return candidate

        return typer(dict.get(self, key))
0
Yonks Somarl

Keine direkte Antwort auf die Frage des OP, aber inspiriert und vielleicht nützlich für einige .. Ich habe eine objektbasierte Lösung mit dem internen __dict__ erstellt (in keiner Weise optimierter Code)

payload = {
    "name": "John",
    "location": {
        "lat": 53.12312312,
        "long": 43.21345112
    },
    "numbers": [
        {
            "role": "home",
            "number": "070-12345678"
        },
        {
            "role": "office",
            "number": "070-12345679"
        }
    ]
}


class Map(object):
    """
    Dot style access to object members, access raw values
    with an underscore e.g.

    class Foo(Map):
        def foo(self):
            return self.get('foo') + 'bar'

    obj = Foo(**{'foo': 'foo'})

    obj.foo => 'foobar'
    obj._foo => 'foo'

    """

    def __init__(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self.__dict__[k] = v
                    self.__dict__['_' + k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self.__dict__[k] = v
                self.__dict__['_' + k] = v

    def __getattribute__(self, attr):
        if hasattr(self, 'get_' + attr):
            return object.__getattribute__(self, 'get_' + attr)()
        else:
            return object.__getattribute__(self, attr)

    def get(self, key):
        try:
            return self.__dict__.get('get_' + key)()
        except (AttributeError, TypeError):
            return self.__dict__.get(key)

    def __repr__(self):
        return u"<{name} object>".format(
            name=self.__class__.__name__
        )


class Number(Map):
    def get_role(self):
        return self.get('role')

    def get_number(self):
        return self.get('number')


class Location(Map):
    def get_latitude(self):
        return self.get('lat') + 1

    def get_longitude(self):
        return self.get('long') + 1


class Item(Map):
    def get_name(self):
        return self.get('name') + " Doe"

    def get_location(self):
        return Location(**self.get('location'))

    def get_numbers(self):
        return [Number(**n) for n in self.get('numbers')]


# Tests

obj = Item({'foo': 'bar'}, **payload)

assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112

for n in obj.numbers:
    assert type(n) == Number
    if n.role == 'home':
        assert n.number == "070-12345678"
    if n.role == 'office':
        assert n.number == "070-12345679"
0