Chuyển đổi dict Python lồng vào đối tượng?


538

Tôi đang tìm kiếm một cách thanh lịch để có được dữ liệu bằng cách sử dụng quyền truy cập thuộc tính trên một dict với một số danh sách và danh sách lồng nhau (ví dụ cú pháp đối tượng kiểu javascript).

Ví dụ:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}

Nên truy cập theo cách này:

>>> x = dict2obj(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
bar

Tôi nghĩ rằng, điều này là không thể nếu không có đệ quy, nhưng điều gì sẽ là một cách hay để có được một kiểu đối tượng cho các ký tự?


6
Gần đây tôi đã cố gắng làm một cái gì đó tương tự, nhưng một khóa từ điển định kỳ ("từ" - đó là một từ khóa Python) đã ngăn tôi vượt qua nó. Bởi vì ngay khi bạn thử sử dụng "x.from" để truy cập thuộc tính đó, bạn sẽ gặp lỗi cú pháp.
Dawie Strauss

3
Đó thực sự là một vấn đề, nhưng tôi có thể từ bỏ "từ" để làm cho cuộc sống dễ dàng hơn trong việc truy cập các cấu trúc chính tả lớn :) gõ x ['a'] ['d'] [1] ['foo'] thực sự gây phiền nhiễu, vì vậy xad [1] .foo quy tắc. nếu bạn cần từ, bạn có thể truy cập nó thông qua getattr (x, 'from') hoặc sử dụng _from làm thuộc tính thay thế.
Marc

5
from_thay vì _fromtheo PEP 8 .
Kos

1
Bạn có thể sử dụng getattr(x, 'from')thay vì đổi tên thuộc tính.
George V. Reilly

3
Hầu hết các "giải pháp" này dường như không hoạt động (ngay cả giải pháp được chấp nhận, không cho phép lồng nhau d1.b.c), tôi nghĩ rõ ràng bạn nên sử dụng một cái gì đó từ thư viện, ví dụ như tên của bộ sưu tập , như câu trả lời này cho thấy, .. .
Andy Hayden

Câu trả lời:


660

Cập nhật: Trong Python 2.6 trở đi, hãy xem xét namedtuplecấu trúc dữ liệu có phù hợp với nhu cầu của bạn không:

>>> from collections import namedtuple
>>> MyStruct = namedtuple('MyStruct', 'a b d')
>>> s = MyStruct(a=1, b={'c': 2}, d=['hi'])
>>> s
MyStruct(a=1, b={'c': 2}, d=['hi'])
>>> s.a
1
>>> s.b
{'c': 2}
>>> s.c
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyStruct' object has no attribute 'c'
>>> s.d
['hi']

Thay thế (nội dung câu trả lời ban đầu) là:

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

Sau đó, bạn có thể sử dụng:

>>> args = {'a': 1, 'b': 2}
>>> s = Struct(**args)
>>> s
<__main__.Struct instance at 0x01D6A738>
>>> s.a
1
>>> s.b
2

19
Tương tự ở đây - điều này đặc biệt hữu ích để xây dựng lại các đối tượng Python từ cơ sở dữ liệu hướng tài liệu như MongoDB.
mikemaccana

13
Để có được bản in đẹp hơn, hãy thêm: def repr __ (self): return '<% s>'% str ('\ n' .join ('% s:% s'% (k, repr (v)) cho (k, v ) trong bản thân .__ dict .iteritems ()))
six8

15
Điều này sẽ làm việc với các từ điển lồng nhau? và dicts có chứa các đối tượng và hoặc danh sách, vv Có bất kỳ bắt?
Sam Stoelinga

5
@Sam S: nó sẽ không tạo ra các lồng nhau Structtừ các từ điển lồng nhau, nhưng nói chung loại giá trị có thể là bất cứ thứ gì. Khóa bị hạn chế để phù hợp với vị trí đối tượng
Eli Bendersky

51
-1 vì a) nó không làm việc cho dicts lồng nhau, đó là câu hỏi rõ ràng cho, và b) các chức năng tương tự hiện đang tồn tại trong các thư viện chuẩn trong argparse.Namespace(mà cũng đã xác định __eq__, __ne__, __contains__).
wim

110
class obj(object):
    def __init__(self, d):
        for a, b in d.items():
            if isinstance(b, (list, tuple)):
               setattr(self, a, [obj(x) if isinstance(x, dict) else x for x in b])
            else:
               setattr(self, a, obj(b) if isinstance(b, dict) else b)

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = obj(d)
>>> x.b.c
2
>>> x.d[1].foo
'bar'

7
Tốt Tuy nhiên, tôi sẽ thay thế .items () bằng .iteritems () để có dung lượng bộ nhớ nhỏ hơn.
Eric O Lebigot

7
Nếu không phải là yêu cầu của OP, đây không phải là vấn đề - nhưng lưu ý rằng điều này sẽ không xử lý đệ quy các đối tượng trong danh sách trong danh sách.
Anon

3
Tôi nhận ra đây là một câu trả lời cũ, nhưng ngày nay sẽ tốt hơn nếu sử dụng một lớp cơ sở trừu tượng thay vì dòng xấu xí đóif isinstance(b, (list, tuple)):
wim

4
Mẹo: Yêu cầu objlớp kế thừa từ argparse.Namespacecác tính năng bổ sung như biểu diễn chuỗi có thể đọc được.
Serrano

2
Tùy thuộc vào trường hợp sử dụng, thông thường chúng tôi sẽ kiểm tra xem đó không phải là loại chuỗi mà đó loại Chuỗi
wim

108

Đáng ngạc nhiên là không ai nhắc đến Bunch . Thư viện này được dành riêng để cung cấp quyền truy cập kiểu thuộc tính cho các đối tượng dict và thực hiện chính xác những gì OP muốn. Một cuộc biểu tình:

>>> from bunch import bunchify
>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = bunchify(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
'bar'

Thư viện Python 3 có sẵn tại https://github.com/Infinidat/munch - Tín dụng được chuyển đến codyzu


16
Và có một nhánh python 3 tương thích (dường như là 2.6-3.4) của Bunch có tên Munch: github.com/Infinidat/munch
codyzu

Bunch là giải pháp tốt nhất trong số này vì nó hỗ trợ tốt nhiều loại tuần tự hóa và được duy trì. Tuyệt vời cảm ơn bạn. Tôi ước phiên bản python3 được đặt tên giống như vậy, vì lý do tại sao không.
Jonathan

4
Vì vậy, chỉ cần một lời cảnh báo trước, Bunch và Attrdict cũng cho vấn đề đó là rất chậm. Họ đã tiêu thụ khoảng 1/3 và 1/2 thời gian yêu cầu của tôi tương ứng khi họ thấy sử dụng thường xuyên trong ứng dụng của chúng tôi. Chắc chắn không có gì để bỏ qua. Nói thêm về nó stackoverflow.com/a/31569634/364604 .
JayD3e

Mặc dù điều này không cho phép truy cập giống như đối tượng vào các dicts, nhưng nó lại thông qua getattr . Điều này làm cho việc hướng nội trong các REPL thông minh như IPython trở nên khó khăn.
GDorn

Tò mò làm thế nào điều này so sánh với JSONpath github.com/kennknles/python-jsonpath-rw
MarkHu

64
x = type('new_dict', (object,), d)

sau đó thêm đệ quy vào đây và bạn đã hoàn thành.

chỉnh sửa đây là cách tôi thực hiện nó:

>>> d
{'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
>>> def obj_dic(d):
    top = type('new', (object,), d)
    seqs = tuple, list, set, frozenset
    for i, j in d.items():
        if isinstance(j, dict):
            setattr(top, i, obj_dic(j))
        elif isinstance(j, seqs):
            setattr(top, i, 
                type(j)(obj_dic(sj) if isinstance(sj, dict) else sj for sj in j))
        else:
            setattr(top, i, j)
    return top

>>> x = obj_dic(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
'bar'

1
Tại sao bạn tạo các đối tượng kiểu và không khởi tạo chúng? Điều đó có hợp lý hơn không? Ý tôi là, tại sao không làm top_instance = top()và trở về nơi bạn trở về top?
bánh kếp

6
Rất tốt cho dữ liệu "chiếc lá", nhưng các ví dụ thuận tiện bỏ đi "cành cây" xx.btrở lại xấu xí<class '__main__.new'>
MarkHu

46

Có một người trợ giúp bộ sưu tập được gọi namedtuple, có thể làm điều này cho bạn:

from collections import namedtuple

d_named = namedtuple('Struct', d.keys())(*d.values())

In [7]: d_named
Out[7]: Struct(a=1, b={'c': 2}, d=['hi', {'foo': 'bar'}])

In [8]: d_named.a
Out[8]: 1

28
Điều này không trả lời câu hỏi đệ quy cho các ký tự lồng nhau.
đủ điều kiện

4
bạn không thể sửa đổi tên được đặt tên.
Tối đa

Chúng tôi có thể đảm bảo rằng thứ tự của danh sách được trả về bởi các khóa () và giá trị () khớp với thứ tự không? Ý tôi là, nếu đó là OrderedDict, vâng. Nhưng một tiêu chuẩn? Tôi biết rằng các lệnh "nhỏ gọn" của CPython 3.6+ và PyPy đã được đặt hàng, nhưng trích dẫn tài liệu: "Khía cạnh giữ trật tự của việc triển khai mới này được coi là một chi tiết thực hiện và không nên dựa vào"
Havok

38
class Struct(object):
    """Comment removed"""
    def __init__(self, data):
        for name, value in data.iteritems():
            setattr(self, name, self._wrap(value))

    def _wrap(self, value):
        if isinstance(value, (tuple, list, set, frozenset)): 
            return type(value)([self._wrap(v) for v in value])
        else:
            return Struct(value) if isinstance(value, dict) else value

Có thể được sử dụng với bất kỳ cấu trúc chuỗi / dict / value ở bất kỳ độ sâu nào.


4
Đây nên là câu trả lời. Nó hoạt động tốt để làm tổ. Bạn cũng có thể sử dụng điều này như một object_hook cho json.load ().
010110110101

2
Tương tự như câu trả lời chức năng của SilentGhost từ năm 2009 - dữ liệu nút lá có thể truy cập được, nhưng cha / twigs hiển thị dưới dạng tham chiếu đối tượng. Để in đẹp,def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
MarkHu

5
Người dùng Python 3.x: nó chỉ .items()thay vì .iteritems()trong dòng 4. (Chức năng đã được đổi tên, nhưng thực chất là giống nhau )
tân bài hiện đại

Hoạt động hoàn hảo cho các đối tượng lồng nhau, cảm ơn! Là một người mới nhận xét như tôi, đối với python3 iteritems () phải được đổi thành vật phẩm ()
Óscar Andreu

31

Lấy những gì tôi cảm thấy là những khía cạnh tốt nhất của các ví dụ trước, đây là những gì tôi nghĩ ra:

class Struct:
  '''The recursive class for building and representing objects with.'''
  def __init__(self, obj):
    for k, v in obj.iteritems():
      if isinstance(v, dict):
        setattr(self, k, Struct(v))
      else:
        setattr(self, k, v)
  def __getitem__(self, val):
    return self.__dict__[val]
  def __repr__(self):
    return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for
      (k, v) in self.__dict__.iteritems()))

Lưu ý rằng hàm tạo có thể được rút ngắn thành: def __init__(self, dct): for k, v in dct.iteritems(): setattr(self, k, isinstance(v, dict) and self.__class__(v) or v) cũng loại bỏ cuộc gọi rõ ràng đếnStruct
George V. Reilly

2
Tôi không muốn hạ thấp câu trả lời của riêng tôi, nhưng nhìn lại điều này tôi nhận thấy rằng nó không tái diễn thành các loại trình tự. xd [1] .foo thất bại trong trường hợp này.
andyvanee

2
các isinstance(v, dict)kiểm tra sẽ tốt hơn là isinstance(v, collections.Mapping)để nó có thể xử lý trong tương lai điều dict-like
Hobs

29

Nếu dict của bạn đến từ json.loads(), bạn có thể biến nó thành một đối tượng thay vì (chứ không phải là một dict) trong một dòng:

import json
from collections import namedtuple

json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))

Xem thêm Cách chuyển đổi dữ liệu JSON thành đối tượng Python .


Có vẻ chậm hơn nhiều so với thông thường .loadsnếu bạn có một đối tượng JSON khổng lồ
michaelsnowden

16

Nếu bạn muốn truy cập các khóa dict như một đối tượng (hoặc là một dict cho các khóa khó), hãy thực hiện đệ quy và cũng có thể cập nhật dict gốc, bạn có thể làm:

class Dictate(object):
    """Object view of a dict, updating the passed in dict when values are set
    or deleted. "Dictate" the contents of a dict...: """

    def __init__(self, d):
        # since __setattr__ is overridden, self.__dict = d doesn't work
        object.__setattr__(self, '_Dictate__dict', d)

    # Dictionary-like access / updates
    def __getitem__(self, name):
        value = self.__dict[name]
        if isinstance(value, dict):  # recursively view sub-dicts as objects
            value = Dictate(value)
        return value

    def __setitem__(self, name, value):
        self.__dict[name] = value
    def __delitem__(self, name):
        del self.__dict[name]

    # Object-like access / updates
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        self[name] = value
    def __delattr__(self, name):
        del self[name]

    def __repr__(self):
        return "%s(%r)" % (type(self).__name__, self.__dict)
    def __str__(self):
        return str(self.__dict)

Ví dụ sử dụng:

d = {'a': 'b', 1: 2}
dd = Dictate(d)
assert dd.a == 'b'  # Access like an object
assert dd[1] == 2  # Access like a dict
# Updates affect d
dd.c = 'd'
assert d['c'] == 'd'
del dd.a
del dd[1]
# Inner dicts are mapped
dd.e = {}
dd.e.f = 'g'
assert dd['e'].f == 'g'
assert d == {'c': 'd', 'e': {'f': 'g'}}

13
>>> def dict2obj(d):
        if isinstance(d, list):
            d = [dict2obj(x) for x in d]
        if not isinstance(d, dict):
            return d
        class C(object):
            pass
        o = C()
        for k in d:
            o.__dict__[k] = dict2obj(d[k])
        return o


>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = dict2obj(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
'bar'

12

Cuối cùng tôi đã thử CẢ HAI AttrDictBunchthư viện và thấy chúng quá chậm đối với 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,

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.


9

x.__dict__.update(d) nên làm tốt


cảm ơn câu trả lời của bạn, nhưng x là gì? dict hay một đối tượng tiêu chuẩn? bạn có thể cho tôi một gợi ý xin vui lòng.
Marc

x là đối tượng của bạn. Mỗi đối tượng có một dict . Bằng cách cập nhật dict của một đối tượng, bạn thực sự đang cập nhật các vars key trong đó.
Alex Coleues

Các từ in đậm là _ _ dict _ _
Alex Coleues

15
Điều này sẽ không xử lý từ điển lồng nhau.
FogleBird

Không phải mọi đối tượng đều có __dict__: thử object().__dict__và bạn sẽ nhận đượcAttributeError: 'object' object has no attribute '__dict__'
Will Manley

8

Bạn có thể tận dụng jsonmô-đun của thư viện chuẩn bằng một móc đối tượng tùy chỉnh :

import json

class obj(object):
    def __init__(self, dict_):
        self.__dict__.update(dict_)

def dict2obj(d):
    return json.loads(json.dumps(d), object_hook=obj)

Ví dụ sử dụng:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
>>> o = dict2obj(d)
>>> o.a
1
>>> o.b.c
2
>>> o.d[0]
u'hi'
>>> o.d[1].foo
u'bar'

Và nó không hoàn toàn chỉ đọc khi có namedtuple, tức là bạn có thể thay đổi giá trị - không phải cấu trúc:

>>> o.b.c = 3
>>> o.b.c
3

1
Câu trả lời hay nhất cho đến nay
Connor

Tôi thích ý tưởng sử dụng cơ chế tải json cho các phần tử lồng nhau. Nhưng chúng ta đã có một lệnh và tôi không thích thực tế là người ta cần tạo một chuỗi từ đó để ánh xạ nó thành một đối tượng. Tôi muốn có một giải pháp tạo ra các đối tượng trực tiếp từ một lệnh.
Sandro

1
Cần chuyển đổi thành chuỗi đầu tiên nhưng khá chung chung vì người ta có thể mở rộng nó qua json.JSONEncoderobject_hook.
Sandro

6

Điều này sẽ giúp bạn bắt đầu:

class dict2obj(object):
    def __init__(self, d):
        self.__dict__['d'] = d

    def __getattr__(self, key):
        value = self.__dict__['d'][key]
        if type(value) == type({}):
            return dict2obj(value)

        return value

d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}

x = dict2obj(d)
print x.a
print x.b.c
print x.d[1].foo

Nó không hoạt động cho danh sách, chưa. Bạn sẽ phải bọc các danh sách trong Danh sách người dùng và quá tải __getitem__để bọc các ký tự.


2
Để làm cho nó hoạt động cho danh sách, sử dụng if isinstance(d, list)mệnh đề từ Anoncâu trả lời của.
Vinay Sajip

6

Tôi biết đã có rất nhiều câu trả lời ở đây và tôi đến bữa tiệc muộn nhưng phương pháp này sẽ được đệ quy và 'tại chỗ' chuyển đổi một từ điển thành một cấu trúc giống như đối tượng ... Hoạt động trong 3.xx

def dictToObject(d):
    for k,v in d.items():
        if isinstance(v, dict):
            d[k] = dictToObject(v)
    return namedtuple('object', d.keys())(*d.values())

# Dictionary created from JSON file
d = {
    'primaryKey': 'id', 
    'metadata': 
        {
            'rows': 0, 
            'lastID': 0
        }, 
    'columns': 
        {
            'col2': {
                'dataType': 'string', 
                'name': 'addressLine1'
            }, 
            'col1': {
                'datatype': 'string', 
                'name': 'postcode'
            }, 
            'col3': {
                'dataType': 'string', 
                'name': 'addressLine2'
            }, 
            'col0': {
                'datatype': 'integer', 
                'name': 'id'
            }, 
            'col4': {
                'dataType': 'string', 
                'name': 'contactNumber'
            }
        }, 
        'secondaryKeys': {}
}

d1 = dictToObject(d)
d1.columns.col1 # == object(datatype='string', name='postcode')
d1.metadata.rows # == 0

"Cấu trúc giống như đối tượng" nghĩa là gì?
MoralCode

1
Đối tượng giống như vì nguyên tắc gõ vịt. Ý tôi là bạn có thể truy cập các thuộc tính của nó như bạn muốn nếu đó là một thể hiện của một lớp. nhưng nó KHÔNG phải là một đối tượng theo nghĩa đó, nó là một bộ dữ liệu có tên.
Aidan Haddon-Wright

5
from mock import Mock
d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
my_data = Mock(**d)

# We got
# my_data.a == 1

4

Hãy để tôi giải thích một giải pháp tôi gần như đã sử dụng một thời gian trước đây. Nhưng trước tiên, lý do tôi đã không được minh họa bởi thực tế là đoạn mã sau:

d = {'from': 1}
x = dict2obj(d)

print x.from

đưa ra lỗi này:

  File "test.py", line 20
    print x.from == 1
                ^
SyntaxError: invalid syntax

Bởi vì "từ" là một từ khóa Python có một số khóa từ điển nhất định mà bạn không thể cho phép.


Bây giờ giải pháp của tôi cho phép truy cập vào các mục từ điển bằng cách sử dụng tên của họ trực tiếp. Nhưng nó cũng cho phép bạn sử dụng "ngữ nghĩa từ điển". Đây là mã với ví dụ sử dụng:

class dict2obj(dict):
    def __init__(self, dict_):
        super(dict2obj, self).__init__(dict_)
        for key in self:
            item = self[key]
            if isinstance(item, list):
                for idx, it in enumerate(item):
                    if isinstance(it, dict):
                        item[idx] = dict2obj(it)
            elif isinstance(item, dict):
                self[key] = dict2obj(item)

    def __getattr__(self, key):
        return self[key]

d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}

x = dict2obj(d)

assert x.a == x['a'] == 1
assert x.b.c == x['b']['c'] == 2
assert x.d[1].foo == x['d'][1]['foo'] == "bar"

1
class Struct: def init __ (self, ** entry): self .__ dict .update (entry)
Kenneth Reitz

4

Hỏi và đáp cũ, nhưng tôi có thêm vài điều để nói. Có vẻ như không ai nói về dict đệ quy. Đây là mã của tôi:

#!/usr/bin/env python

class Object( dict ):
    def __init__( self, data = None ):
        super( Object, self ).__init__()
        if data:
            self.__update( data, {} )

    def __update( self, data, did ):
        dataid = id(data)
        did[ dataid ] = self

        for k in data:
            dkid = id(data[k])
            if did.has_key(dkid):
                self[k] = did[dkid]
            elif isinstance( data[k], Object ):
                self[k] = data[k]
            elif isinstance( data[k], dict ):
                obj = Object()
                obj.__update( data[k], did )
                self[k] = obj
                obj = None
            else:
                self[k] = data[k]

    def __getattr__( self, key ):
        return self.get( key, None )

    def __setattr__( self, key, value ):
        if isinstance(value,dict):
            self[key] = Object( value )
        else:
            self[key] = value

    def update( self, *args ):
        for obj in args:
            for k in obj:
                if isinstance(obj[k],dict):
                    self[k] = Object( obj[k] )
                else:
                    self[k] = obj[k]
        return self

    def merge( self, *args ):
        for obj in args:
            for k in obj:
                if self.has_key(k):
                    if isinstance(self[k],list) and isinstance(obj[k],list):
                        self[k] += obj[k]
                    elif isinstance(self[k],list):
                        self[k].append( obj[k] )
                    elif isinstance(obj[k],list):
                        self[k] = [self[k]] + obj[k]
                    elif isinstance(self[k],Object) and isinstance(obj[k],Object):
                        self[k].merge( obj[k] )
                    elif isinstance(self[k],Object) and isinstance(obj[k],dict):
                        self[k].merge( obj[k] )
                    else:
                        self[k] = [ self[k], obj[k] ]
                else:
                    if isinstance(obj[k],dict):
                        self[k] = Object( obj[k] )
                    else:
                        self[k] = obj[k]
        return self

def test01():
    class UObject( Object ):
        pass
    obj = Object({1:2})
    d = {}
    d.update({
        "a": 1,
        "b": {
            "c": 2,
            "d": [ 3, 4, 5 ],
            "e": [ [6,7], (8,9) ],
            "self": d,
        },
        1: 10,
        "1": 11,
        "obj": obj,
    })
    x = UObject(d)


    assert x.a == x["a"] == 1
    assert x.b.c == x["b"]["c"] == 2
    assert x.b.d[0] == 3
    assert x.b.d[1] == 4
    assert x.b.e[0][0] == 6
    assert x.b.e[1][0] == 8
    assert x[1] == 10
    assert x["1"] == 11
    assert x[1] != x["1"]
    assert id(x) == id(x.b.self.b.self) == id(x.b.self)
    assert x.b.self.a == x.b.self.b.self.a == 1

    x.x = 12
    assert x.x == x["x"] == 12
    x.y = {"a":13,"b":[14,15]}
    assert x.y.a == 13
    assert x.y.b[0] == 14

def test02():
    x = Object({
        "a": {
            "b": 1,
            "c": [ 2, 3 ]
        },
        1: 6,
        2: [ 8, 9 ],
        3: 11,
    })
    y = Object({
        "a": {
            "b": 4,
            "c": [ 5 ]
        },
        1: 7,
        2: 10,
        3: [ 12 , 13 ],
    })
    z = {
        3: 14,
        2: 15,
        "a": {
            "b": 16,
            "c": 17,
        }
    }
    x.merge( y, z )
    assert 2 in x.a.c
    assert 3 in x.a.c
    assert 5 in x.a.c
    assert 1 in x.a.b
    assert 4 in x.a.b
    assert 8 in x[2]
    assert 9 in x[2]
    assert 10 in x[2]
    assert 11 in x[3]
    assert 12 in x[3]
    assert 13 in x[3]
    assert 14 in x[3]
    assert 15 in x[2]
    assert 16 in x.a.b
    assert 17 in x.a.c

if __name__ == '__main__':
    test01()
    test02()

4

Muốn tải lên phiên bản của tôi về mô hình nhỏ này.

class Struct(dict):
  def __init__(self,data):
    for key, value in data.items():
      if isinstance(value, dict):
        setattr(self, key, Struct(value))
      else:   
        setattr(self, key, type(value).__init__(value))

      dict.__init__(self,data)

Nó bảo tồn các thuộc tính cho loại được nhập vào lớp. Mối quan tâm duy nhất của tôi sẽ là ghi đè các phương thức từ trong từ điển phân tích cú pháp của bạn. Nhưng nếu không thì có vẻ vững chắc!


Mặc dù là một ý tưởng hay, nhưng điều này dường như không hoạt động đối với ví dụ của OP, nhưng thực tế nó dường như sửa đổi thông tin trong từ điển! Trong thực tế, df.athậm chí không làm việc.
Andy Hayden

4

Điều này cũng hoạt động tốt

class DObj(object):
    pass

dobj = Dobj()
dobj.__dict__ = {'a': 'aaa', 'b': 'bbb'}

print dobj.a
>>> aaa
print dobj.b
>>> bbb

3

Đây là một cách khác để thực hiện đề xuất ban đầu của SilentGhost:

def dict2obj(d):
  if isinstance(d, dict):
    n = {}
    for item in d:
      if isinstance(d[item], dict):
        n[item] = dict2obj(d[item])
      elif isinstance(d[item], (list, tuple)):
        n[item] = [dict2obj(elem) for elem in d[item]]
      else:
        n[item] = d[item]
    return type('obj_from_dict', (object,), n)
  else:
    return d

3

Tôi tình cờ phát hiện ra trường hợp tôi cần chuyển đổi một cách đệ quy một danh sách các ký tự thành danh sách các đối tượng, vì vậy dựa trên đoạn trích của Roberto ở đây, công việc đã làm cho tôi:

def dict2obj(d):
    if isinstance(d, dict):
        n = {}
        for item in d:
            if isinstance(d[item], dict):
                n[item] = dict2obj(d[item])
            elif isinstance(d[item], (list, tuple)):
                n[item] = [dict2obj(elem) for elem in d[item]]
            else:
                n[item] = d[item]
        return type('obj_from_dict', (object,), n)
    elif isinstance(d, (list, tuple,)):
        l = []
        for item in d:
            l.append(dict2obj(item))
        return l
    else:
        return d

Lưu ý rằng bất kỳ bộ dữ liệu nào sẽ được chuyển đổi thành danh sách tương đương, vì những lý do rõ ràng.

Hy vọng điều này sẽ giúp ai đó nhiều như tất cả các câu trả lời của bạn đã làm cho tôi, các bạn.


3

Điều gì về việc chỉ định của bạn dictcho __dict__một đối tượng trống?

class Object:
    """If your dict is "flat", this is a simple way to create an object from a dict

    >>> obj = Object()
    >>> obj.__dict__ = d
    >>> d.a
    1
    """
    pass

Tất nhiên điều này không thành công trên ví dụ dict lồng nhau của bạn trừ khi bạn đi theo dict đệ quy:

# For a nested dict, you need to recursively update __dict__
def dict2obj(d):
    """Convert a dict to an object

    >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    >>> obj = dict2obj(d)
    >>> obj.b.c
    2
    >>> obj.d
    ["hi", {'foo': "bar"}]
    """
    try:
        d = dict(d)
    except (TypeError, ValueError):
        return d
    obj = Object()
    for k, v in d.iteritems():
        obj.__dict__[k] = dict2obj(v)
    return obj

Và phần tử danh sách ví dụ của bạn có thể có nghĩa là Mappingmột danh sách các cặp (khóa, giá trị) như thế này:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': [("hi", {'foo': "bar"})]}
>>> obj = dict2obj(d)
>>> obj.d.hi.foo
"bar"

2

Đây là một triển khai khác:

class DictObj(object):
    def __init__(self, d):
        self.__dict__ = d

def dict_to_obj(d):
    if isinstance(d, (list, tuple)): return map(dict_to_obj, d)
    elif not isinstance(d, dict): return d
    return DictObj(dict((k, dict_to_obj(v)) for (k,v) in d.iteritems()))

[Chỉnh sửa] Mất một chút về việc xử lý các ký tự trong danh sách, không chỉ các ký tự khác. Đã thêm sửa chữa.


Lưu ý rằng việc đặt dict cho từ điển nguồn có nghĩa là mọi thay đổi đối với các thuộc tính trên đối tượng kết quả cũng sẽ ảnh hưởng đến từ điển đã tạo đối tượng và ngược lại. Điều này có thể dẫn đến kết quả không mong muốn nếu từ điển được sử dụng cho mục đích khác ngoài việc tạo đối tượng.
Mark Roddy

@Mark: Trên thực tế, một từ điển mới đang được chuyển đến DictObj mỗi lần, thay vì chỉ đi qua cùng một đối tượng chính tả, vì vậy điều này sẽ không thực sự xảy ra. Không cần thiết phải làm điều này, vì tôi cũng cần dịch các giá trị trong từ điển, do đó không thể đi qua đối tượng chính tả ban đầu mà không tự làm biến đổi nó.
Brian

Có khá nhiều câu trả lời cho điều này, câu trả lời được chấp nhận không giống như nó sẽ là đệ quy hoặc xử lý danh sách. Tôi đã đọc qua tất cả và cái này trông đơn giản nhất ngay từ cái nhìn đầu tiên, tôi đã thử nghiệm và nó đã hoạt động. Câu trả lời chính xác.
Luôn giao dịch

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

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

    def copy(self):
        return Struct(dict.copy(self))

Sử dụng:

points = Struct(x=1, y=2)
# Changing
points['x'] = 2
points.y = 1
# Accessing
points['x'], points.x, points.get('x') # 2 2 2
points['y'], points.y, points.get('y') # 1 1 1
# Accessing inexistent keys/attrs 
points['z'] # KeyError: z
points.z # AttributeError: z
# Copying
points_copy = points.copy()
points.x = 2
points_copy.x # 1

2

Còn cái này thì sao:

from functools import partial
d2o=partial(type, "d2o", ())

Điều này sau đó có thể được sử dụng như thế này:

>>> o=d2o({"a" : 5, "b" : 3})
>>> print o.a
5
>>> print o.b
3

2

Tôi nghĩ rằng một dict bao gồm số, chuỗi và dict là đủ thời gian nhất. Vì vậy, tôi bỏ qua tình huống mà các bộ dữ liệu, danh sách và các loại khác không xuất hiện trong chiều cuối cùng của một lệnh.

Xem xét tính kế thừa, kết hợp với đệ quy, nó giải quyết vấn đề in một cách thuận tiện và cũng cung cấp hai cách để truy vấn dữ liệu, một cách để chỉnh sửa dữ liệu.

Xem ví dụ dưới đây, một bản chính tả mô tả một số thông tin về sinh viên:

group=["class1","class2","class3","class4",]
rank=["rank1","rank2","rank3","rank4","rank5",]
data=["name","sex","height","weight","score"]

#build a dict based on the lists above
student_dic=dict([(g,dict([(r,dict([(d,'') for d in data])) for r in rank ]))for g in group])

#this is the solution
class dic2class(dict):
    def __init__(self, dic):
        for key,val in dic.items():
            self.__dict__[key]=self[key]=dic2class(val) if isinstance(val,dict) else val


student_class=dic2class(student_dic)

#one way to edit:
student_class.class1.rank1['sex']='male'
student_class.class1.rank1['name']='Nan Xiang'

#two ways to query:
print student_class.class1.rank1
print student_class.class1['rank1']
print '-'*50
for rank in student_class.class1:
    print getattr(student_class.class1,rank)

Các kết quả:

{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
--------------------------------------------------
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}

2

Thông thường, bạn muốn phản ánh hệ thống phân cấp chính tả vào đối tượng của mình nhưng không liệt kê hoặc bộ dữ liệu thường ở mức thấp nhất. Vì vậy, đây là cách tôi đã làm điều này:

class defDictToObject(object):

    def __init__(self, myDict):
        for key, value in myDict.items():
            if type(value) == dict:
                setattr(self, key, defDictToObject(value))
            else:
                setattr(self, key, value)

Vì vậy chúng tôi làm:

myDict = { 'a': 1,
           'b': { 
              'b1': {'x': 1,
                    'y': 2} },
           'c': ['hi', 'bar'] 
         }

và lấy:

x.b.b1.x 1

x.c ['hi', 'thanh']


1

Từ điển của tôi có định dạng này:

addr_bk = {
    'person': [
        {'name': 'Andrew', 'id': 123, 'email': 'andrew@mailserver.com',
         'phone': [{'type': 2, 'number': '633311122'},
                   {'type': 0, 'number': '97788665'}]
        },
        {'name': 'Tom', 'id': 456,
         'phone': [{'type': 0, 'number': '91122334'}]}, 
        {'name': 'Jack', 'id': 7788, 'email': 'jack@gmail.com'}
    ]
}

Có thể thấy, tôi có các từ điển lồng nhaudanh sách các từ điển . Điều này là do addr_bk đã được giải mã từ dữ liệu bộ đệm giao thức được chuyển đổi thành một python dict bằng lwpb.codec. Có trường tùy chọn (ví dụ: email => trong đó khóa có thể không khả dụng) và trường lặp lại (ví dụ: phone => được chuyển đổi thành danh sách chính tả).

Tôi đã thử tất cả các giải pháp đề xuất ở trên. Một số không xử lý tốt các từ điển lồng nhau. Những người khác không thể in chi tiết đối tượng một cách dễ dàng.

Chỉ có giải pháp, dict2obj (dict) của Dawie Strauss, hoạt động tốt nhất.

Tôi đã tăng cường một chút để xử lý khi không thể tìm thấy khóa:

# Work the best, with nested dictionaries & lists! :)
# Able to print out all items.
class dict2obj_new(dict):
    def __init__(self, dict_):
        super(dict2obj_new, self).__init__(dict_)
        for key in self:
            item = self[key]
            if isinstance(item, list):
                for idx, it in enumerate(item):
                    if isinstance(it, dict):
                        item[idx] = dict2obj_new(it)
            elif isinstance(item, dict):
                self[key] = dict2obj_new(item)

    def __getattr__(self, key):
        # Enhanced to handle key not found.
        if self.has_key(key):
            return self[key]
        else:
            return None

Sau đó, tôi đã thử nghiệm nó với:

# Testing...
ab = dict2obj_new(addr_bk)

for person in ab.person:
  print "Person ID:", person.id
  print "  Name:", person.name
  # Check if optional field is available before printing.
  if person.email:
    print "  E-mail address:", person.email

  # Check if optional field is available before printing.
  if person.phone:
    for phone_number in person.phone:
      if phone_number.type == codec.enums.PhoneType.MOBILE:
        print "  Mobile phone #:",
      elif phone_number.type == codec.enums.PhoneType.HOME:
        print "  Home phone #:",
      else:
        print "  Work phone #:",
      print phone_number.number

1

Xây dựng câu trả lời của tôi cho " python: Làm thế nào để thêm tài sản vào một lớp một cách linh hoạt? ":

class data(object):
    def __init__(self,*args,**argd):
        self.__dict__.update(dict(*args,**argd))

def makedata(d):
    d2 = {}
    for n in d:
        d2[n] = trydata(d[n])
    return data(d2)

def trydata(o):
    if isinstance(o,dict):
        return makedata(o)
    elif isinstance(o,list):
        return [trydata(i) for i in o]
    else:
        return o

Bạn gọi makedatatừ điển bạn muốn chuyển đổi, hoặc có thể trydatatùy thuộc vào những gì bạn mong đợi là đầu vào và nó tạo ra một đối tượng dữ liệu.

Ghi chú:

  • Bạn có thể thêm elif vào trydatanếu bạn cần thêm chức năng.
  • Rõ ràng điều này sẽ không hoạt động nếu bạn muốn x.a = {}hoặc tương tự.
  • Nếu bạn muốn có một phiên bản chỉ đọc, hãy sử dụng dữ liệu lớp từ câu trả lời ban đầu .
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.