วิธีใช้จุด“.” การเข้าถึงสมาชิกพจนานุกรม?


283

ฉันจะทำให้สมาชิกพจนานุกรม Python สามารถเข้าถึงได้ผ่านจุด "." อย่างไร

ตัวอย่างเช่นแทนที่จะของการเขียนmydict['val'], mydict.valผมอยากจะเขียน

นอกจากนี้ฉันต้องการเข้าถึง dicts ที่ซ้อนกันด้วยวิธีนี้ ตัวอย่างเช่น

mydict.mydict2.val 

จะหมายถึง

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

20
หลายสถานการณ์ที่ผู้คนใช้ dicts ซ้อนกันจะเป็นเช่นเดียวกับดีหรือดีกว่าโดยมีการเสิร์ฟ dicts กับ tuples เป็นกุญแจที่จะถูกแทนที่ด้วยd[a][b][c] d[a, b, c]
Mike Graham

7
ไม่ใช่เวทมนต์: foo = {}; foo [1,2,3] = "หนึ่ง, สอง, สาม!"; foo.keys () => [(1,2,3)]
Bryan Oakley

10
ว้าว. ว้าวอีกครั้ง ฉันไม่รู้ว่า tuples อาจเป็นกุญแจของ dict ว้าวครั้งที่สาม
bodacydo

3
วัตถุใด ๆ ที่เป็น "hashable" อาจถูกใช้เป็นกุญแจของ dict วัตถุที่เปลี่ยนไม่ได้ส่วนใหญ่ยังแฮชได้ แต่ถ้าเนื้อหาทั้งหมดนั้นแฮช รหัส d [1, 2, 3] ใช้งานได้เพราะ "," คือ "สร้างตัวดำเนินการ tuple"; มันเหมือนกับ d [(1, 2, 3)] วงเล็บมักเป็นทางเลือกรอบการประกาศของ tuple
Larry Hastings

6
คุณเคยพิจารณากรณีที่สำคัญมีจุดด้วยตัวเอง - {"my.key":"value"}? หรือเมื่อคีย์เป็นคำหลักเช่น "จาก" ฉันคิดว่ามันสองสามครั้งและมันเป็นปัญหาและการแก้ไขปัญหามากกว่าการรับรู้ประโยชน์
Todor Minakov

คำตอบ:


147

คุณสามารถทำได้โดยใช้คลาสนี้ที่ฉันเพิ่งทำ ด้วยคลาสนี้คุณสามารถใช้Mapวัตถุเช่นพจนานุกรมอื่น (รวมถึงการทำให้เป็นอนุกรม json) หรือด้วยเครื่องหมายจุด ฉันหวังว่าจะช่วยคุณ:

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]

ตัวอย่างการใช้งาน:

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
ในการทำงานกับ Python 3 ฉันได้อัปเดต.iteritems()เป็น.items()
berto

13
โปรดทราบว่าสิ่งนี้จะทำงานแตกต่างจากความคาดหวังทั่วไปซึ่งจะไม่เพิ่มขึ้นAttributeErrorหากไม่มีแอตทริบิวต์ มันจะกลับมาNoneแทน
mic_e

แนะนำให้เพิ่มgetstateและsetstateเพื่อให้การคัดลอกแบบลึกและระบบอื่น ๆ สามารถรองรับได้
user1363990

4
self.update(*args,**kwargs)คุณสามารถลดความซับซ้อนของตัวสร้างของคุณเพื่อ __missing__(self,key): value=self[key]= type(self)(); return valueนอกจากนี้คุณยังสามารถเพิ่ม จากนั้นคุณสามารถเพิ่มรายการที่หายไปโดยใช้เครื่องหมายจุด หากคุณต้องการให้สามารถเลือกได้คุณสามารถเพิ่ม__getstate__และ__setstate__
Jens Munk

1
สิ่งนี้จะทำให้hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding __getattr__`
เซี่ยว

265

ฉันเก็บมันไว้รอบ ๆ ในไฟล์ util คุณสามารถใช้มันเป็น mixin ในชั้นเรียนของคุณเองได้เช่นกัน

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
คำตอบที่ง่ายมากเยี่ยมมาก! คุณรู้หรือไม่ว่าฉันต้องทำอะไรเพื่อให้การทำงานของแท็บเสร็จสมบูรณ์ใน IPython ชั้นจะต้องใช้ __dir __ (ตัวเอง) แต่อย่างใดฉันก็ไม่สามารถทำงานได้
andreas-h

8
+1 เพื่อความง่าย แต่ดูเหมือนจะไม่ทำงานบน dicts ที่ซ้อนกัน d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.barพ่นข้อผิดพลาดคุณลักษณะ แต่d.fooทำงานได้ดี
tmthyjames

2
ใช่นี่ใช้ไม่ได้กับโครงสร้างที่ซ้อนกันที่ซับซ้อน
David

16
@tmthyjames คุณสามารถคืนค่าวัตถุชนิด dotdict ในวิธี getter เพื่อเข้าถึงแอ็ตทริบิวต์ซ้ำโดยใช้เครื่องหมายจุดเช่น: 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
หลังจากทดลองกับมันดูเหมือนว่าgetเป็นความคิดที่ไม่ดีเนื่องจากมันจะกลับมาNoneแทนที่จะเพิ่มข้อผิดพลาดสำหรับรายการที่ขาดหายไป ...
NichtJens

117

ติดตั้งdotmapผ่านpip

pip install dotmap

มันทำทุกสิ่งที่คุณต้องการให้ทำและคลาสย่อยdictดังนั้นมันจึงทำงานเหมือนพจนานุกรมปกติ:

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'

ยิ่งไปกว่านั้นคุณสามารถแปลงเป็นและจากdictวัตถุ:

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

ซึ่งหมายความว่าหากสิ่งที่คุณต้องการเข้าถึงมีอยู่แล้วในdictแบบฟอร์มคุณสามารถเปลี่ยนให้เป็นแบบDotMapเข้าถึงได้ง่าย

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

ในที่สุดมันจะสร้างDotMapอินสแตนซ์ลูกใหม่โดยอัตโนมัติเพื่อให้คุณสามารถทำสิ่งนี้:

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

เปรียบเทียบกับพวง

การเปิดเผยข้อมูล: I am ผู้สร้างของDotMap ฉันสร้างมันเพราะBunchคุณสมบัติเหล่านี้หายไป

  • การจดจำรายการคำสั่งซื้อจะถูกเพิ่มและวนซ้ำในลำดับนั้น
  • DotMapการสร้างลูกโดยอัตโนมัติซึ่งช่วยประหยัดเวลาและสร้างรหัสที่สะอาดขึ้นเมื่อคุณมีลำดับชั้นจำนวนมาก
  • การสร้างจาก a dictและแปลงdictอินสแตนซ์ย่อยทั้งหมดเป็นDotMap

2
:-) คุณสามารถทำให้มันทำงานกับปุ่มที่มีจุดอยู่ในชื่อแล้ว? {"test.foo": "bar"}สามารถเข้าถึงได้ผ่านทางmymap.test.fooที่จะยอดเยี่ยม มันจะใช้เวลาถดถอยในการแปลงแผนที่แบนเป็นแผนที่ลึกจากนั้นใช้ DotMap กับมัน แต่มันก็คุ้มค่า!
dlite922

เรียบร้อย มีวิธีใดที่จะทำให้การแสดงรายการแท็บ / เสร็จสมบูรณ์ทำงานร่วมกับปุ่มต่างๆในสมุดบันทึก Jupyter การเข้าถึงสไตล์ดอทนั้นมีค่าที่สุดสำหรับการใช้งานแบบโต้ตอบ
Dmitri

@Dmitri Cool ผลิตภัณฑ์ ไม่เคยได้ยินมาก่อนดังนั้นฉันไม่แน่ใจว่าจะทำให้การเติมข้อความอัตโนมัติทำงานอย่างไร ฉันเห็นด้วยDotMapกับการเติมข้อความอัตโนมัติให้ดีที่สุด ฉันใช้ข้อความประเสริฐซึ่งการเติมข้อความอัตโนมัติคำหลักที่พิมพ์ไว้ก่อนหน้านี้
Chris Redford

1
ผมพบว่ามันขาดสกัดพจนานุกรมสำหรับสิ่งที่ต้องการหรือ**kwargs c = {**a, **b}ในความเป็นจริงมันล้มเหลวอย่างเงียบ ๆ มันทำตัวเหมือนพจนานุกรมเปล่าเมื่อทำการแตกไฟล์
Simon Streicher

@SimonStreicher ผมทดสอบนี้กับผมได้ที่คาดว่าจะm = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m)); 2 3หากคุณมีกรณีที่พิสูจน์แล้วว่าใช้งานdict()ไม่ได้DotMap()โปรดส่งรหัสของคุณไปที่แท็บปัญหาใน GitHub
Chris Redford

56

เป็นผลมาจาก Dict และและดำเนินการและ__getattr____setattr__

หรือคุณสามารถใช้Bunchซึ่งคล้ายกันมาก

ฉันไม่คิดว่ามันจะเป็นไปได้ที่จะจับลิงในชุด dict ในตัว


2
monkeypatch หมายถึงอะไรกันแน่? ฉันเคยได้ยินเกี่ยวกับเรื่องนี้ แต่ไม่ได้ใช้ (ขออภัยที่ฉันถามคำถามเกี่ยวกับมือใหม่ฉันยังไม่ค่อยเก่งเรื่องการเขียนโปรแกรม (ฉันเป็นนักเรียนปี 2 เท่านั้น))
bodacydo

9
Monkeypatching ใช้พลังของ Python (หรือภาษาใด ๆ ) เพื่อเปลี่ยนแปลงบางอย่างที่มักจะถูกกำหนดในซอร์สโค้ด โดยเฉพาะอย่างยิ่งใช้กับการเปลี่ยนคำจำกัดความของคลาสหลังจากที่ถูกสร้างขึ้น
Mike Graham

หากคุณใช้ฟังก์ชั่นนี้บ่อยๆจงระวังความเร็วของพวง ฉันใช้มันบ่อยมากและท้ายที่สุดก็ใช้เวลาหนึ่งในสามของคำขอของฉัน ตรวจสอบคำตอบของฉันสำหรับคำอธิบายรายละเอียดเพิ่มเติมนี้
JayD3e

22

เนื้อผ้ามีการใช้งานที่ดีและใช้งานน้อยมาก การขยายเพื่ออนุญาตการเข้าถึงแบบซ้อนเราสามารถใช้ a defaultdictและผลลัพธ์มีลักษณะดังนี้:

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

ใช้ประโยชน์จากมันดังต่อไปนี้:

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

นั่นอธิบายรายละเอียดเล็กน้อยเกี่ยวกับคำตอบของ Kugel เกี่ยวกับ"Derive from dict และ and Implement __getattr__and __setattr__" ตอนนี้คุณรู้แล้ว!


1
อันนั้นยอดเยี่ยม!
Thomas Klinger

ดีที่จะรวม defaultdict - อย่างไรก็ตามสิ่งนี้ดูเหมือนว่าจะใช้ได้เฉพาะเมื่อเริ่มต้น dict ตั้งแต่เริ่มต้น หากเราต้องการแปลง dict ที่มีอยู่เป็น "dotdict" แบบเรียกซ้ำ นี่เป็นอีกทางเลือกหนึ่งdotdictที่อนุญาตให้แปลงdictวัตถุที่มีอยู่ซ้ำ ๆ : gist.github.com/miku/…
miku

19

ฉันลองสิ่งนี้:

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

คุณสามารถลอง __getattribute__เช่นกัน

ทำให้ทุก dict ชนิดของ dotdict น่าจะเพียงพอถ้าคุณต้องการเริ่มจาก dict แบบหลายเลเยอร์ลองใช้__init__เช่นกัน


อ๊ะคำตอบของ @Kugel นั้นคล้ายกัน
tdihp

1
อย่างไรก็ตาม, ฉันชอบคำตอบของคุณเพราะฉันเข้าใจได้เร็วขึ้น - มันมีรหัสจริง
yigal

1
+1 สำหรับรหัสจริง แต่ข้อเสนอแนะของ @ Kugel เกี่ยวกับการใช้ Bunch ก็ดีเช่นกัน
Dannid

ฉันพบว่ามีประโยชน์ในการฝังฟังก์ชันนี้ไว้ข้างในฟังก์ชั่นโดยวางdef docdict(name):ก่อนหน้าแล้ว `ถ้า isinstance (ชื่อ, dict): return DotDict (ชื่อ) ชื่อส่งคืน`
Daniel Moskovich

ตัวอย่างง่ายๆที่ดี .. ฉันขยายนี้เล็กน้อยเพื่อให้ dict ซ้อนกันถูกล่ามโซ่ได้อย่างง่ายดายคล้ายกับ @DanielMoskovich แต่ยังส่งกลับโหนดใบอย่างถูกต้องสำหรับ int, สตริง ฯลฯ ... หรือ null ถ้าไม่พบclass 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

อย่า การเข้าถึงแอททริบิวต์และการจัดทำดัชนีเป็นสิ่งแยกต่างหากใน Python และคุณไม่ควรต้องการให้พวกเขาทำเช่นเดียวกัน สร้างคลาส (อาจทำโดยnamedtuple) หากคุณมีบางอย่างที่ควรมีแอตทริบิวต์ที่เข้าถึงได้และใช้[]สัญลักษณ์เพื่อรับไอเท็มจาก dict


ขอบคุณสำหรับคำตอบ. แต่ลองดูคำถามนี้ที่ฉันเพิ่งถามมา: stackoverflow.com/questions/2352252/ ...... นี่เป็นความคิดที่ดีที่จะใช้.แทนที่จะ[]เข้าถึงโครงสร้างข้อมูลที่ซับซ้อนในเทมเพลต Mako
bodacydo

2
ฉันสามารถดูกรณีการใช้งานสำหรับสิ่งนี้; อันที่จริงฉันเพิ่งทำเมื่อสองสามสัปดาห์ก่อน ในกรณีของฉันฉันต้องการวัตถุที่ฉันสามารถเข้าถึงคุณลักษณะด้วยเครื่องหมายจุด ฉันพบว่ามันง่ายมากที่จะสืบทอดจาก dict ดังนั้นฉันจึงได้คุณสมบัติ dict ทั้งหมดในตัว แต่ส่วนต่อประสานสาธารณะกับวัตถุนี้ใช้เครื่องหมายจุด (เป็นส่วนติดต่อแบบอ่านอย่างเดียวกับข้อมูลคงที่) ผู้ใช้ของฉันมีความสุขมากกับ 'foo.bar' มากกว่า 'foo ["bar"]' และฉันดีใจที่ฉันสามารถดึงคุณสมบัติของประเภทข้อมูล dict ออกมาได้
Bryan Oakley

10
คุณรู้จักสไตล์ Python ดีอยู่แล้ว: เรากำลังบอกคุณอย่าแสร้งว่าค่าของ dict นั้นเป็นคุณลักษณะ มันเป็นการฝึกฝนที่ไม่ดี ตัวอย่างเช่นถ้าคุณต้องการเก็บค่าที่มีชื่อเดียวกันกับแอตทริบิวต์ที่มีอยู่ของ dict เช่น "items" หรือ "get" หรือ "pop" อาจเป็นสิ่งที่ทำให้เกิดความสับสน ดังนั้นอย่าทำ!
Larry Hastings

5
โอ๊ะโอฉันลืมเกี่ยวกับคุณลักษณะเช่น 'items', 'get' หรือ 'pop ขอบคุณที่ยกตัวอย่างที่สำคัญนี้!
bodacydo

5
@ เกบมันนานมากแล้ว ... แต่ฉันคิดว่ามันคุ้มค่าที่จะพูด มันไม่ "ดีพอใน JS": มันคือ "น่ากลัวพอใน JS" มันจะตลกเมื่อคุณเก็บคีย์ / attr ที่มีชื่อเดียวกันกับคุณลักษณะที่สำคัญอื่น ๆ ในห่วงโซ่ต้นแบบ
bgusach

11

หากคุณต้องการดองพจนานุกรมที่แก้ไขแล้วของคุณคุณต้องเพิ่มวิธีการบางอย่างในคำตอบข้างต้น:

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

ขอบคุณสำหรับความคิดเห็นเกี่ยวกับการดอง ฉันรู้สึกคลั่งไคล้ในข้อผิดพลาดนี้และเพิ่งรู้ว่าเป็นเพราะปัญหานี้!
Shagru

ยังเกิดขึ้นเมื่อคุณใช้ copy.deepcopy การเพิ่มนี้เป็นสิ่งจำเป็น
user1363990

การทำให้เข้าใจง่าย:__getattr__ = dict.get
martineau

9

การสร้างคำตอบของ Kugel และนำคำเตือนอย่างรอบคอบของ Mike Graham มาพิจารณาเราจะทำเสื้อคลุมอย่างไร?

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

การใช้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
วิธีนี้ใช้ได้ผลดีกว่า (กับ JSON โหลดจากแฟ้ม)
ged

บัญชีนี้ใช้สำหรับวางซ้อนกันหรือไม่?
Mojimi

1
ไม่รองรับ Dict ที่ซ้อนกัน docs.python.org/3.3/library/types.html#types.SimpleNamespace
Carson

6

ฉันชอบMunchและให้ตัวเลือกที่มีประโยชน์มากมายบนจุดเข้าถึง

นำเข้าแทะเล็ม

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

dict_munch = munch.munchify (temp_1)

dict_munch.person.fname


6

ฉันเพิ่งเจอกล่อง 'ห้องสมุด ' ' ซึ่งทำสิ่งเดียวกัน

คำสั่งการติดตั้ง: pip install python-box

ตัวอย่าง:

from box import Box

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

print(mydict.key1.v1)

ฉันพบว่ามันจะมีประสิทธิภาพมากกว่าไลบรารีอื่น ๆ ที่มีอยู่เช่น dotmap ซึ่งสร้างข้อผิดพลาดการเรียกซ้ำของ python เมื่อคุณมี dicts ที่ซ้อนกันขนาดใหญ่

ลิงก์ไปยังห้องสมุดและรายละเอียด: https://pypi.org/project/python-box/


5

ใช้__getattr__ง่ายมากใช้งานได้ใน 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)

เอาท์พุท:

10000
StackOverflow

4

ภาษาเองไม่รองรับสิ่งนี้ แต่บางครั้งก็ยังเป็นข้อกำหนดที่มีประโยชน์ นอกจากสูตร Bunch คุณยังสามารถเขียนวิธีการเล็กน้อยซึ่งสามารถเข้าถึงพจนานุกรมโดยใช้สตริงจุด:

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

ซึ่งจะสนับสนุนสิ่งนี้:

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

4

ในการสร้างตามคำตอบของ epool รุ่นนี้ให้คุณเข้าถึง dict ภายในผ่านโอเปอเรเตอร์ dot:

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

ยกตัวอย่างเช่นผลตอบแทนfoo.bar.baz[1].baba"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: แทนที่iteritems()ด้วยitems()และxrange()ด้วยrange()
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)

หากใครตัดสินใจที่จะแปลงสิ่งนั้นdictเป็นวัตถุอย่างถาวรสิ่งนี้ควรทำ คุณสามารถสร้างวัตถุที่ถูกเหวี่ยงได้ก่อนที่จะเข้าถึง

d = dict_to_object(d)

def attr (** kwargs): o = lambda: ไม่มี o .__ dict __. update (** kwargs) return o
throws_exceptions_at_you

2

ในที่สุดฉันก็ลองทั้งAttrDictและBunchห้องสมุดและพบว่าพวกเขาเป็นวิธีที่ช้าสำหรับการใช้งานของฉัน หลังจากเพื่อนและฉันดูเข้าไปแล้วเราพบว่าวิธีการหลักในการเขียนไลบรารีเหล่านี้ส่งผลให้ห้องสมุดซ้ำซากผ่านวัตถุที่ซ้อนกันและทำการคัดลอกวัตถุพจนานุกรมไปทั่ว ด้วยความคิดนี้เราจึงทำการเปลี่ยนแปลงที่สำคัญสองประการ 1) เราสร้างคุณลักษณะที่โหลดขี้เกียจ 2) แทนที่จะสร้างสำเนาของวัตถุพจนานุกรมเราสร้างสำเนาของวัตถุพร็อกซี่น้ำหนักเบา นี่คือการดำเนินการขั้นสุดท้าย การเพิ่มประสิทธิภาพของการใช้รหัสนี้ไม่น่าเชื่อ เมื่อใช้ AttrDict หรือ Bunch ห้องสมุดสองแห่งนี้ใช้เวลาเพียง 1/2 และ 1/3 ตามลำดับในการร้องขอของฉัน (what !?) รหัสนี้ลดเวลานั้นแทบจะไม่มีอะไรเลย (อยู่ในช่วง 0.5ms) หลักสูตรนี้ขึ้นอยู่กับความต้องการของคุณ แต่ถ้าคุณใช้ฟังก์ชันนี้ค่อนข้างน้อยในรหัสของคุณ

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

ดูการดำเนินงานเดิมที่นี่โดยhttps://stackoverflow.com/users/704327/michael-merickel

สิ่งอื่นที่ควรทราบคือการใช้งานนี้ค่อนข้างง่ายและไม่ได้ใช้วิธีการทั้งหมดที่คุณอาจต้องการ คุณจะต้องเขียนสิ่งเหล่านี้ตามที่ต้องการบนวัตถุ DictProxy หรือ ListProxy


0

ฉันต้องการที่จะโยนทางออกของตัวเองเข้าไปในแหวน:

https://github.com/skorokithakis/jsane

ช่วยให้คุณสามารถวิเคราะห์ JSON เป็นสิ่งที่คุณสามารถเข้าถึงwith.attribute.lookups.like.this.r()ได้ส่วนใหญ่เป็นเพราะฉันไม่เห็นคำตอบนี้ก่อนที่จะเริ่มทำงาน


Python มีความผิดพลาดในการออกแบบที่เรียบง่ายไม่กี่ข้อผิดพลาดการเพิ่มKeyErrorเป็นหนึ่งในนั้นเมื่อผู้ใช้เข้าถึงกุญแจที่ไม่มีอยู่ทั้งหมดสิ่งที่ต้องทำคือกลับคืนมาNoneคล้ายกับพฤติกรรมของ JS ฉันเป็นแฟนตัวยงของการปรับปรุงอัตโนมัติทั้งการอ่านและการเขียน ห้องสมุดของคุณอยู่ใกล้กับอุดมคติที่สุด
nehem

0

ไม่ได้เป็นคำตอบที่ตรงกับคำถาม OP แต่แรงบันดาลใจจากและบางทีอาจจะมีประโยชน์สำหรับบาง .. ฉันได้สร้างโซลูชั่นที่วัตถุโดยใช้ภายใน__dict__(ในโค้ดไม่มีวิธีที่ดีที่สุด)

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

วิธีง่ายๆในการรับการเข้าถึงจุด (แต่ไม่ใช่การเข้าถึงอาร์เรย์) คือการใช้วัตถุธรรมดาใน Python แบบนี้:

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

... และใช้งานแบบนี้:

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

... เพื่อแปลงเป็น dict:

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

0

โซลูชันนี้เป็นการปรับแต่งตามที่เสนอโดยepoolเพื่อตอบสนองความต้องการของ OP ในการเข้าถึง dicts ที่ซ้อนกันในลักษณะที่สอดคล้องกัน โซลูชันโดย epool ไม่อนุญาตให้เข้าถึง dicts ที่ซ้อนกัน

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]

A.B.C.Dด้วยชั้นนี้ได้ในขณะนี้สามารถทำสิ่งที่ชอบ:


0

วิธีนี้ใช้ได้กับ dicts ที่ซ้อนกันและทำให้แน่ใจว่า dicts ที่ถูกต่อท้ายทำงานในลักษณะเดียวกัน:

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

คำตอบของ @ derek73นั้นเนี้ยบมาก แต่ไม่สามารถดองหรือคัดลอก (ลึก) ได้และจะส่งกลับNoneสำหรับคีย์ที่หายไป รหัสด้านล่างแก้ไขสิ่งนี้

แก้ไข:ฉันไม่เห็นคำตอบข้างต้นที่กล่าวถึงจุดเดียวกันที่แน่นอน (upvoted) ฉันทิ้งคำตอบไว้ที่นี่เพื่อการอ้างอิง

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

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

-1

วิธีการแก้ปัญหาชนิดที่ละเอียดอ่อน

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))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.