我如何使Python字典成员访问通过点“。”?

例如,我想写mydict.val而不是mydict['val']。

我还想以这种方式访问嵌套字典。例如

mydict.mydict2.val 

会提到

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

当前回答

最简单的解决方案。

定义一个只有pass语句的类。为该类创建对象并使用点表示法。

class my_dict:
    pass

person = my_dict()
person.id = 1 # create using dot notation
person.phone = 9999
del person.phone # Remove a property using dot notation

name_data = my_dict()
name_data.first_name = 'Arnold'
name_data.last_name = 'Schwarzenegger'

person.name = name_data
person.name.first_name # dot notation access for nested properties - gives Arnold

其他回答

语言本身不支持这一点,但有时这仍然是一个有用的需求。除了Bunch recipe,你还可以写一个小方法,可以使用虚线字符串访问字典:

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

你可以用我刚做的这个类来做。对于这个类,您可以像使用另一个字典(包括json序列化)一样使用Map对象,或者使用点表示法。希望对大家有所帮助:

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

基于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

此解决方案是对epool提供的解决方案的改进,以满足OP以一致的方式访问嵌套字典的需求。epool的解决方案不允许访问嵌套字典。

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.

这也适用于嵌套字典,并确保后面追加的字典行为相同:

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__