Cách sử dụng dấu chấm. để truy cập các thành viên của từ điển?


282

Làm cách nào để tôi có thể truy cập các thành viên từ điển Python thông qua dấu chấm "."?

Ví dụ, thay vì viết mydict['val'], tôi muốn viết mydict.val.

Ngoài ra, tôi muốn truy cập các dicts lồng nhau theo cách này. Ví dụ

mydict.mydict2.val 

sẽ đề cập đến

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

20
Nhiều tình huống trong đó mọi người sử dụng các dicts lồng nhau cũng sẽ được phục vụ tốt hơn hoặc tốt hơn bởi các dicts với bộ dữ liệu làm chìa khóa, nơi d[a][b][c]được thay thế bằng d[a, b, c].
Mike Graham

7
Đó không phải là phép thuật: foo = {}; foo [1,2,3] = "một, hai, ba!"; foo.keys () => [(1,2,3)]
Bryan Oakley

10
Ồ Wow một lần nữa. Tôi không biết tuples có thể là chìa khóa của dict. Wow lần thứ ba.
Bodacydo

3
Bất kỳ đối tượng nào là "có thể băm" có thể được sử dụng làm khóa của một lệnh. Hầu hết các đối tượng bất biến cũng có thể băm, nhưng chỉ khi tất cả nội dung của chúng có thể băm được. Mã d [1, 2, 3] hoạt động vì "," là "tạo toán tử tuple"; nó giống như d [(1, 2, 3)]. Dấu ngoặc đơn thường là tùy chọn xung quanh việc khai báo một tuple.
Larry Hastings

6
Bạn đã xem xét trường hợp khóa có một dấu chấm - {"my.key":"value"}chưa? Hoặc khi khóa là một từ khóa, như "từ"? Tôi đã xem xét nó một vài lần, và nó có nhiều vấn đề và xử lý sự cố hơn là lợi ích nhận thấy.
Todor Minakov

Câu trả lời:


147

Bạn có thể làm điều đó bằng cách sử dụng lớp này tôi vừa thực hiện. Với lớp này, bạn có thể sử dụng Mapđối tượng như một từ điển khác (bao gồm cả tuần tự hóa json) hoặc với ký hiệu dấu chấm. Tôi hy vọng sẽ giúp bạn:

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]

Ví dụ sử dụng:

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']

21
Để làm việc trên Python 3, tôi đã cập nhật .iteritems()lên.items()
berto

13
Lưu ý rằng điều này sẽ hành xử khác với những kỳ vọng chung ở chỗ nó sẽ không tăng AttributeErrornếu thuộc tính không tồn tại. Thay vào đó nó sẽ trở lại None.
mic_e

Đề nghị thêm getstatesetstate để sao chép sâu và các hệ thống khác có thể hỗ trợ nó.
dùng1363990

4
Bạn có thể đơn giản hóa hàm tạo của bạn self.update(*args,**kwargs). Ngoài ra, bạn có thể thêm __missing__(self,key): value=self[key]= type(self)(); return value. Sau đó, bạn có thể thêm các mục bị thiếu bằng cách sử dụng ký hiệu chấm. Nếu bạn muốn nó có thể được chọn, bạn có thể thêm __getstate____setstate__
Jens Munk

1
Điều này sẽ khiến hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding __getattr__`
Xiao

264

Tôi đã luôn giữ điều này xung quanh trong một tập tin. Bạn cũng có thể sử dụng nó như một mixin trên các lớp của riêng bạn.

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'

5
Câu trả lời rất đơn giản, tuyệt vời! Bạn có tình cờ biết tôi cần phải làm gì để hoàn thành tab trong công việc IPython không? Lớp sẽ cần triển khai __dir __ (tự), nhưng bằng cách nào đó tôi không thể làm cho nó hoạt động được.
andreas-h

8
+1 cho đơn giản. nhưng dường như không hoạt động trên các dicts lồng nhau. d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.barném một lỗi thuộc tính, nhưng d.foohoạt động tốt.
tmthyjames

2
Đúng, điều này không làm việc cho các cấu trúc lồng nhau phức tạp.
David

16
@tmthyjames bạn chỉ có thể trả về đối tượng loại dotdict trong phương thức getter để truy cập đệ quy các thuộc tính với ký hiệu dấu chấm như: python class DotDict(dict): """dot.notation access to dictionary attributes""" def __getattr__(*args): val = dict.get(*args) return DotDict(val) if type(val) is dict else val __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__
TMKasun

4
Sau khi thử nghiệm với nó, có vẻ như đó getthực sự là một ý tưởng tồi vì nó sẽ quay trở lại Nonethay vì gây ra lỗi cho các mục bị thiếu ...
NichtJens

117

Cài đặt dotmapquapip

pip install dotmap

Nó thực hiện mọi thứ bạn muốn nó làm và các lớp con dict, vì vậy nó hoạt động như một từ điển bình thường:

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'

Trên hết, bạn có thể chuyển đổi nó sang và từ dictcác đối tượng:

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

Điều này có nghĩa là nếu một cái gì đó bạn muốn truy cập đã ở dictdạng, bạn có thể biến nó thành một thứ DotMapdễ truy cập:

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

Cuối cùng, nó tự động tạo các thể hiện con mới DotMapđể bạn có thể làm những việc như thế này:

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

So sánh với Bunch

Tiết lộ đầy đủ: Tôi là người tạo ra DotMap . Tôi đã tạo nó vì Bunchthiếu các tính năng này

  • ghi nhớ các mục thứ tự được thêm vào và lặp lại theo thứ tự đó
  • DotMaptạo con tự động , giúp tiết kiệm thời gian và tạo mã sạch hơn khi bạn có nhiều thứ bậc
  • xây dựng từ một dictvà chuyển đổi đệ quy tất cả các dicttrường hợp con thànhDotMap

2
:-) bạn có thể làm cho nó hoạt động với các phím đã được chấm trong tên không? {"test.foo": "bar"}có thể được truy cập thông qua mymap.test.fooĐiều đó sẽ là tuyệt vời. Sẽ mất một số hồi quy để chuyển đổi bản đồ phẳng thành bản đồ sâu sau đó áp dụng DotMap cho nó, nhưng nó đáng giá!
dlite922

Khéo léo. Bất kỳ cách nào để làm cho danh sách / hoàn thành tab hoạt động với các phím trong sổ ghi chép Jupyter? Truy cập kiểu chấm có giá trị nhất để sử dụng tương tác.
Dmitri

@Dmitri Sản phẩm tuyệt vời. Chưa bao giờ nghe về nó trước đây, vì vậy tôi không chắc làm thế nào để nó tự động hoàn thành. Tôi đồng ý sử dụng DotMapvới autocomplete hoạt động tốt nhất. Tôi sử dụng Sublime Text, tự động hoàn thành các từ khóa đã gõ trước đó.
Chris Redford

1
Tôi thấy rằng nó thiếu trích xuất từ ​​điển cho những thứ như **kwargshoặc c = {**a, **b}. Trong thực tế, nó thất bại lặng lẽ, nó hoạt động như một từ điển trống khi giải nén.
Simon Strerich

@SimonStre Rich Tôi đã thử nghiệm điều này với m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m));và tôi đã mong đợi 2 3. Nếu bạn có một trường hợp hỏng đã được chứng minh có hiệu quả dict()nhưng không DotMap(), vui lòng gửi mã của bạn đến tab Sự cố trong GitHub.
Chris Redford

56

Xuất phát từ dict và thực hiện __getattr____setattr__.

Hoặc bạn có thể sử dụng Bunch rất giống nhau.

Tôi không nghĩ rằng nó có thể bắt chước lớp dict tích hợp.


2
Monkeypatch có nghĩa là gì chính xác? Tôi đã nghe nói về nó nhưng không được sử dụng. (Xin lỗi mà tôi đặt câu hỏi người mới như vậy, tôi không phải là tốt với lập trình chưa (tôi chỉ 2 sinh viên năm).)
bodacydo

9
Monkeypatching đang sử dụng tính năng động của Python (hoặc bất kỳ ngôn ngữ nào) để thay đổi thứ gì đó thường được xác định trong mã nguồn. Nó đặc biệt áp dụng để thay đổi định nghĩa của các lớp sau khi chúng được tạo.
Mike Graham

Nếu bạn đang sử dụng chức năng này rất nhiều, hãy cẩn thận với tốc độ của Bunch. Tôi đã sử dụng nó khá thường xuyên và cuối cùng tôi đã tiêu tốn một phần ba thời gian yêu cầu của tôi. Kiểm tra câu trả lời của tôi để được giải thích chi tiết hơn về điều này.
JayD3e

22

Vải có thật sự tốt đẹp, tối thiểu thực hiện . Mở rộng điều đó để cho phép truy cập lồng nhau, chúng ta có thể sử dụng một defaultdictvà kết quả trông giống như thế này:

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

Sử dụng nó như sau:

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

Điều đó giải thích một chút về câu trả lời của Kugel về "Xuất phát từ chính tả và thực hiện __getattr____setattr__". Bây giờ bạn biết làm thế nào!


1
Đó là một trong những tuyệt vời!
Thomas Klinger

Rất vui khi bao gồm một defaultdict - tuy nhiên điều này dường như chỉ hoạt động khi bắt đầu một lệnh từ đầu. Nếu chúng ta cần chuyển đổi một dict hiện có thành "dotdict" theo cách đệ quy. Đây là một giải pháp thay thế dotdictcho phép chuyển đổi dictđệ quy đối tượng hiện có : gist.github.com/miku/ Khăn
miku

19

Tôi đã thử điều này:

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

bạn có thể thử __getattribute__quá.

làm cho mỗi dict một loại dotdict sẽ đủ tốt, nếu bạn muốn khởi tạo nó từ một dict nhiều lớp, hãy thử thực hiện __init__quá.


Rất tiếc, câu trả lời của @Kugel cũng tương tự.
tdihp

1
tdihp, tôi vẫn thích câu trả lời của bạn vì tôi hiểu nó nhanh hơn - nó có mã thực tế.
Yigal

1
+1 cho mã thực tế. Nhưng đề xuất sử dụng Bunch của @ Kugel cũng rất tốt.
Dannid

Tôi thấy hữu ích khi nhúng hàm này vào bên trong một hàm bằng cách đặt def docdict(name):trước nó và sau đó `if isinstance (name, dict): trả về tên trả về DotDict (tên)`
Daniel Moskovich

ví dụ đơn giản tuyệt vời .. Tôi đã mở rộng điều này một chút để một lệnh chính được lồng dễ dàng bị xiềng xích, tương tự như @DanielMoskovich, nhưng cũng trả về các nút lá chính xác cho int, chuỗi, v.v ... hoặc null nếu không tìm thấyclass dotdict(dict): def __getattr__(self, name): if name not in self: return None elif type(self[name]) is dict: return JsonDot(self[name]) else: return self[name]
D Sievers

11

Đừng. Truy cập thuộc tính và lập chỉ mục là những thứ riêng biệt trong Python và bạn không nên muốn chúng thực hiện giống nhau. Tạo một lớp (có thể là một lớp được tạo bởi namedtuple) nếu bạn có thứ gì đó cần có thuộc tính có thể truy cập và sử dụng []ký hiệu để lấy một mục từ một lệnh.


Cảm ơn câu trả lời. Nhưng hãy xem câu hỏi này mà tôi cũng vừa hỏi: stackoverflow.com/questions/2352252/NH Đây có vẻ là một ý tưởng tốt để sử dụng .thay vì []truy cập các cấu trúc dữ liệu phức tạp trong các mẫu Mako.
Bodacydo

2
Tôi có thể thấy một trường hợp sử dụng cho việc này; Trên thực tế, tôi đã làm điều đó chỉ một vài tuần trước. Trong trường hợp của tôi, tôi muốn một đối tượng mà tôi có thể truy cập các thuộc tính bằng ký hiệu dấu chấm. Tôi thấy rất dễ dàng để kế thừa từ dict vì vậy tôi có tất cả các tính năng dict tích hợp, nhưng giao diện công khai cho đối tượng này sử dụng ký hiệu dấu chấm (về cơ bản là giao diện chỉ đọc với một số dữ liệu tĩnh). Người dùng của tôi hạnh phúc hơn với 'foo.bar' so với 'foo ["bar"]' và tôi rất vui khi tôi có thể cõng các tính năng của kiểu dữ liệu chính tả.
Bryan Oakley

10
Bạn đã biết phong cách Python tốt: chúng tôi đang nói với bạn, đừng giả vờ rằng các giá trị của một dict là thuộc tính. Đó là thực tế xấu. Ví dụ: điều gì sẽ xảy ra nếu bạn muốn lưu trữ một giá trị có cùng tên với thuộc tính hiện có của một dict, như "mục" hoặc "get" hoặc "pop"? Có lẽ là một cái gì đó khó hiểu. Vì vậy, đừng làm điều đó!
Larry Hastings

5
Rất tiếc, tôi đã quên các thuộc tính như 'mục', 'get' hoặc 'pop. Cảm ơn đã đưa ra ví dụ quan trọng này!
Bodacydo

5
@Gabe, đã lâu rồi ... nhưng tôi nghĩ nó đáng để nói. Nó không phải là "đủ tốt trong JS": nó là "đủ khủng khiếp trong JS". Thật buồn cười khi bạn lưu trữ các khóa / attr có cùng tên với các thuộc tính quan trọng khác trong chuỗi nguyên mẫu.
bgusach

11

Nếu bạn muốn chọn từ điển đã sửa đổi của mình, bạn cần thêm một vài phương thức trạng thái vào các câu trả lời trên:

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

Cảm ơn các bình luận về dưa chua. Tôi đã phát điên vì lỗi này và chỉ nhận ra rằng đó là do vấn đề này!
Shagru

Cũng xảy ra khi bạn sử dụng copy.deepcopy. Sự bổ sung này là cần thiết.
dùng1363990

Đơn giản hóa:__getattr__ = dict.get
martineau

9

Dựa vào câu trả lời của Kugel và cân nhắc những lời thận trọng của Mike Graham, nếu chúng ta làm một cái bọc thì sao?

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

8

Sử dụng SimpleNamespace:

>>> from types import SimpleNamespace   
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])

1
Cách tiếp cận này hoạt động tốt hơn. (với json được tải từ tệp)
ged

Có tài khoản này cho các dicts lồng nhau?
Mojimi

1
Không hỗ trợ Dict lồng nhau. docs.python.org/3.3/l Library / type.html
Carson

6

Tôi thích Munch và nó cung cấp rất nhiều tùy chọn tiện dụng trên đầu truy cập điểm.

nhập khẩu munch

temp_1 = {'người': {'fname': 'senthil', 'lname': 'ramalingam'}}

dict_munch = munch.munchify (temp_1)

dict_munch.person.fname


6

Gần đây tôi tình cờ thấy thư viện ' Box ' cũng làm điều tương tự.

Lệnh cài đặt: pip install python-box

Thí dụ:

from box import Box

mydict = {"key1":{"v1":0.375,
                    "v2":0.625},
          "key2":0.125,
          }
mydict = Box(mydict)

print(mydict.key1.v1)

Tôi thấy nó hiệu quả hơn các thư viện hiện có khác như dotmap, nó tạo ra lỗi đệ quy python khi bạn có các ký tự lồng nhau lớn.

liên kết đến thư viện và thông tin chi tiết: https://pypi.org/project/python-box/


5

Sử dụng __getattr__, rất đơn giản, hoạt động trong 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)

Đầu ra:

10000
StackOverflow

4

Bản thân ngôn ngữ không hỗ trợ điều này, nhưng đôi khi đây vẫn là một yêu cầu hữu ích. Bên cạnh công thức Bunch, bạn cũng có thể viết một phương thức nhỏ có thể truy cập từ điển bằng cách sử dụng chuỗi chấm:

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

mà sẽ hỗ trợ một cái gì đó như thế này:

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

4

Để xây dựng theo câu trả lời của epool, phiên bản này cho phép bạn truy cập bất kỳ lệnh nào bên trong thông qua toán tử dấu chấm:

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

Ví dụ, foo.bar.baz[1].babatrả về "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]

1
Python 3: thay thế iteritems()bằng items()xrange()bằngrange()
sasawatc

3
def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

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

    return Struct(**dick)

Nếu một người quyết định chuyển đổi vĩnh viễn điều đó dictthành đối tượng thì điều này nên làm. Bạn có thể tạo một đối tượng ném ngay trước khi truy cập.

d = dict_to_object(d)

def attr (** kwargs): o = lambda: Không o .__ dict __. update (** kwargs) return o
throws_exceptions_at_you 31/1/19

2

Cuối cùng tôi đã thử CẢ HAI AttrDictBunchthư viện và tìm thấy chúng là cách để làm chậm việc sử dụng của tôi. Sau khi một người bạn và tôi xem xét nó, chúng tôi thấy rằng phương pháp chính để viết các thư viện này dẫn đến thư viện tích cực đệ quy thông qua một đối tượng lồng nhau và tạo các bản sao của đối tượng từ điển trong suốt. Với suy nghĩ này, chúng tôi đã thực hiện hai thay đổi chính. 1) Chúng tôi tạo các thuộc tính được tải 2) thay vì tạo các bản sao của một đối tượng từ điển, chúng tôi tạo các bản sao của một đối tượng proxy có trọng lượng nhẹ. Đây là việc thực hiện cuối cùng. Hiệu suất tăng của việc sử dụng mã này là không thể tin được. Khi sử dụng AttrDict hoặc Bunch, chỉ riêng hai thư viện này đã tiêu tốn 1/2 và 1/3 thời gian yêu cầu của tôi (cái gì!?). Mã này đã giảm thời gian đó xuống gần như không có gì (ở đâu đó trong phạm vi 0,5ms). Điều này tất nhiên phụ thuộc vào nhu cầu của bạn, nhưng nếu bạn đang sử dụng chức năng này một chút trong mã của bạn,

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

Xem triển khai ban đầu tại đây bởi https://stackoverflow.com/users/704327/michael-merickel .

Một điều khác cần lưu ý là việc triển khai này khá đơn giản và không thực hiện tất cả các phương pháp bạn có thể cần. Bạn sẽ cần phải viết những thứ đó theo yêu cầu trên các đối tượng DictProxy hoặc ListProxy.


0

Tôi muốn ném giải pháp của riêng tôi vào vòng:

https://github.com/skorokithakis/jsane

Nó cho phép bạn phân tích JSON thành thứ gì đó bạn có thể truy cập with.attribute.lookups.like.this.r(), chủ yếu là vì tôi đã không thấy câu trả lời này trước khi bắt đầu làm việc với nó.


Python có lỗi với một vài lỗi thiết kế đơn giản đáng tiếc, nâng cao KeyErrorlà một trong số đó, Khi một người truy cập vào khóa không tồn tại, tất cả những gì phải làm là trả về Nonetương tự như hành vi JS. Tôi là một fan hâm mộ lớn của tự động hóa cả về đọc và viết. Thư viện của bạn là gần nhất với lý tưởng.
nehem

0

Không phải là câu trả lời trực tiếp cho câu hỏi của OP, nhưng được truyền cảm hứng bởi và có lẽ hữu ích cho một số người .. Tôi đã tạo ra một giải pháp dựa trên đối tượng bằng cách sử dụng nội bộ __dict__(Không có cách nào tối ưu hóa mã)

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

Một cách đơn giản để có quyền truy cập dấu chấm (nhưng không phải là truy cập mảng), là sử dụng một đối tượng đơn giản trong Python. Như thế này:

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

... và sử dụng nó như thế này:

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

... để chuyển đổi nó thành một lệnh:

>>> print(obj.__dict__)
{"key": "value"}

0

Giải pháp này là một sàng lọc dựa trên một giải pháp được cung cấp bởi epool để giải quyết yêu cầu của OP để truy cập các bộ đệm lồng nhau một cách nhất quán. Giải pháp của epool không cho phép truy cập vào các dicts lồng nhau.

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]

Với lớp này, giờ đây người ta có thể làm một cái gì đó như : A.B.C.D.


0

Điều này cũng hoạt động với các ký tự lồng nhau và đảm bảo rằng các ký tự được nối sau này hoạt động giống nhau:

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

Câu trả lời của @ derek73 rất gọn gàng, nhưng nó không thể được sao chép hay sao chép (sâu) và nó trả về Nonecho các phím bị thiếu. Mã dưới đây sửa lỗi này.

Chỉnh sửa: Tôi không thấy câu trả lời ở trên có cùng một điểm chính xác (được nâng cấp). Tôi để lại câu trả lời ở đây để tham khảo.

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

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

-1

Một loại giải pháp tinh tế

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))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.