Jak używać kropki "."aby uzyskać dostęp do słownika?

Jak sprawić, by Użytkownicy słownika Pythona byli dostępni przez kropkę"."?

Na przykład, zamiast pisać mydict['val'], chciałbym napisać mydict.val.

Również chciałbym uzyskać dostęp do zagnieżdżonych dictów w ten sposób. Na przykład

mydict.mydict2.val 

Odnosi się do

mydict = { 'mydict2': { 'val': ... } }
Author: Machavity, 2010-02-28

19 answers

Możesz to zrobić używając tej klasy, którą właśnie stworzyłem. Z tą klasą możesz używać obiektu Map Jak innego słownika (włącznie z serializacją json) lub z notacją kropkową. Mam nadzieję Ci pomóc:

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]

Przykłady użycia:

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']
 96
Author: epool,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-07 15:53:23

Zawsze trzymałem to w pliku util. Możesz go używać jako mixin na własnych zajęciach.

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'
 134
Author: derek73,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-01 08:59:25

Install dotmap via pip

pip install dotmap

Robi wszystko, co chcesz i podklasy dict, więc działa jak normalny słownik:

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'

Poza tym możesz konwertować go do i z dict obiektów:

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor
Oznacza to, że jeśli coś, do czego chcesz uzyskać dostęp, znajduje się już w formularzu dict, możesz przekształcić go w DotMap dla łatwego dostępu:
import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

Wreszcie, automatycznie tworzy nowe instancje potomne DotMap, dzięki czemu możesz robić takie rzeczy jak to:

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

Porównanie do Bunch

Pełne ujawnienie: jestem twórcą DotMap . Stworzyłem go, ponieważ Bunch brakowało tych funkcji

  • zapamiętywanie elementów zamówienia są dodawane i powtarzane w tej kolejności
  • automatyczne tworzenie potomka DotMap, które oszczędza czas i sprawia, że kod jest czystszy, gdy masz dużo hierarchii
  • konstruowanie z dict i rekurencyjnie konwertowanie wszystkich instancji potomnych dict na DotMap
 67
Author: Chris Redford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-27 17:41:57

Wywołaj z dict oraz zaimplementuj __getattr__ i __setattr__.

Lub możesz użyć Bunch , który jest bardzo podobny.

Myślę, że nie jest możliwe monkeypatch wbudowanej klasy dict.

 54
Author: Kugel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-28 19:32:20

Próbowałem tego:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]
Ty też możesz spróbować.

Spraw, by każdy dict był typem dotdict, który byłby wystarczająco dobry, jeśli chcesz init to z wielowarstwowego dict, spróbuj zaimplementować __init__.

 16
Author: tdihp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-11 08:45:22

Tkanina ma naprawdę ładne, Minimalne wykonanie . Rozszerzając to, aby umożliwić dostęp zagnieżdżony, możemy użyć defaultdict, a wynik wygląda mniej więcej tak:

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

Użyj go w następujący sposób:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234
W 2007 roku, po raz pierwszy w historii, Kugel otrzymał tytuł mistrza świata w wadze koguciej. teraz wiesz jak!
 9
Author: Dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-05 00:50:28

Jeśli chcesz poprawić swój zmodyfikowany słownik, musisz dodać kilka metod stanowych do powyższych odpowiedzi:

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
 7
Author: volodymyr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-18 14:20:21

Nie. dostęp do atrybutów i indeksowanie są osobnymi rzeczami w Pythonie i nie powinieneś chcieć, aby wykonywały to samo. Stwórz klasę (prawdopodobnie stworzoną przez namedtuple), jeśli masz coś, co powinno mieć dostępne atrybuty i użyj notacji [], Aby uzyskać element z dict.

 6
Author: Mike Graham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-28 19:01:51

Bazując na odpowiedzi Kugela i biorąc pod uwagę Słowa ostrzeżenia Mike ' a Grahama, co jeśli zrobimy opakowanie?

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
Author: Michael A. Jackson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-19 08:28:18

Sam język tego nie obsługuje, ale czasami jest to nadal przydatny wymóg. Poza przepisem Bunch, możesz również napisać małą metodę, która może uzyskać dostęp do słownika za pomocą kropkowanego ciągu:

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

Które wspierałyby coś takiego:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>
 4
Author: pbanka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-23 20:30:34
def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

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

    return Struct(**dick)

Jeśli ktoś zdecyduje się na trwałą konwersję dict na obiekt, powinno to zrobić. Obiekt throwaway można utworzyć tuż przed uzyskaniem dostępu.

d = dict_to_object(d)
 3
Author: nehemiah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-01 04:52:54

Aby bazować na odpowiedzi epool, ta wersja umożliwia dostęp do dowolnego dict wewnątrz za pomocą operatora dot:

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

Na przykład, foo.bar.baz[1].baba zwraca "loo".

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
Author: user4343502,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-31 02:11:18

Lubię Munch i daje wiele przydatnych opcji na górze dostępu do kropki.

Import munch

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

Dict_munch = munch.munchify (temp_1)

Dict_munch.osób.fname

 3
Author: Senthil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-05 16:02:24

Użycie __getattr__, bardzo proste, działa w 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)

Wyjście:

10000
StackOverflow
 2
Author: IRSHAD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-19 07:49:10

W końcu próbowałem zarównoAttrDict jak iBunch bibliotek i stwierdziłem, że są one zbyt powolne dla moich zastosowań. Po tym, jak przyjrzeliśmy się temu z przyjacielem, odkryliśmy, że główna metoda pisania tych bibliotek powoduje, że biblioteka agresywnie rekurencyjnie przenika przez zagnieżdżony obiekt i robi kopie obiektu słownika. Mając to na uwadze, wprowadziliśmy dwie kluczowe zmiany. 1) wykonaliśmy atrybuty lazy-loaded 2) zamiast tworzyć kopie obiektu słownika, tworzymy kopie lekkiego obiektu proxy. Jest to ostateczne wdrożenie. Wzrost wydajności korzystania z tego kodu jest niesamowity. Podczas korzystania z AttrDict lub Bunch, te dwie biblioteki same pochłonęły odpowiednio 1/2 i 1/3 czasu mojego żądania (co!?). Kod ten skrócił ten czas do prawie nic(gdzieś w zakresie 0,5 ms). To oczywiście zależy od twoich potrzeb, ale jeśli korzystasz z tej funkcji dość dużo w kodzie, zdecydowanie wybierz coś prostego, takiego jak to.

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

Zobacz oryginalną implementację tutaj by https://stackoverflow.com/users/704327/michael-merickel .

Inną rzeczą, którą należy zauważyć, jest to, że ta implementacja jest dość prosta i nie implementuje wszystkich metod, których możesz potrzebować. Musisz zapisać je zgodnie z wymaganiami w obiektach DictProxy lub ListProxy.

 1
Author: JayD3e,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:14

Chciałbym wrzucić własne rozwiązanie do ringu:

Https://github.com/skorokithakis/jsane

Pozwala na parsowanie JSON do czegoś, do czego można uzyskać dostęp with.attribute.lookups.like.this.r(), głównie dlatego, że nie widziałem tej odpowiedzi przed rozpoczęciem pracy nad nim.

 0
Author: Stavros Korokithakis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-04 14:41:36

Nie jest to bezpośrednia odpowiedź na pytanie OP, ale inspirowane i być może przydatne dla niektórych.. Stworzyłem rozwiązanie obiektowe przy użyciu wewnętrznego __dict__ (w żaden sposób zoptymalizowany kod)

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
Author: Hedde van der Heide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-31 07:48:00

Jednym z prostych sposobów na uzyskanie dostępu do kropki (ale nie dostępu do tablicy) jest użycie zwykłego obiektu w Pythonie. Tak:

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

...i używaj go tak:

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

... aby go przekonwertować na dict:

>>> print(obj.__dict__)
{"key": "value"}
 0
Author: Emil Stenström,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-08 06:46:47

To rozwiązanie jest udoskonaleniem oferowanym przez epool w celu sprostania wymaganiom OP, aby uzyskać dostęp do zagnieżdżonych dictów w spójny sposób. Rozwiązanie epool nie pozwalało na dostęp do zagnieżdżonych dictów.

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]

Z tą klasą można teraz zrobić coś w stylu: A.B.C.D.

 0
Author: deepak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-03 07:22:31