【问题标题】:Using JSON keys as attributes in nested JSON使用 JSON 键作为嵌套 JSON 中的属性
【发布时间】:2012-04-05 10:22:06
【问题描述】:

我在 python 2.7 中使用嵌套的类似 JSON 的数据结构,我与一些外国 perl 代码交换。我只想以更 Python 的方式“处理”这些列表和字典的嵌套结构。

所以如果我有这样的结构...

a = {
    'x': 4,
    'y': [2, 3, { 'a': 55, 'b': 66 }],
}

...我希望能够在 python 脚本中处理它,就好像它是嵌套的 python 类/结构一样,如下所示:

>>> aa = j2p(a)  # <<- this is what I'm after.
>>> print aa.x
4
>>> aa.z = 99
>>> print a
{
    'x': 4,
    'y': [2, 3, { 'a': 55, 'b': 66 }],
    'z': 99
}

>>> aa.y[2].b = 999

>>> print a
{
    'x': 4,
    'y': [2, 3, { 'a': 55, 'b': 999 }],
    'z': 99
}

因此 aa 是原始结构的代理。这是我到目前为止的想法,灵感来自优秀的What is a metaclass in Python? 问题。

def j2p(x):
    """j2p creates a pythonic interface to nested arrays and
    dictionaries, as returned by json readers.

    >>> a = { 'x':[5,8], 'y':5}
    >>> aa = j2p(a)
    >>> aa.y=7
    >>> print a
    {'x': [5, 8], 'y':7}
    >>> aa.x[1]=99
    >>> print a
    {'x': [5, 99], 'y':7}

    >>> aa.x[0] = {'g':5, 'h':9}
    >>> print a
    {'x': [ {'g':5, 'h':9} , 99], 'y':7}
    >>> print aa.x[0].g
    5
    """
    if isinstance(x, list):
        return _list_proxy(x)
    elif isinstance(x, dict):
        return _dict_proxy(x)
    else:
        return x

class _list_proxy(object):
    def __init__(self, proxied_list):
        object.__setattr__(self, 'data', proxied_list)
    def __getitem__(self, a):
        return j2p(object.__getattribute__(self, 'data').__getitem__(a))
    def __setitem__(self, a, v):
        return object.__getattribute__(self, 'data').__setitem__(a, v)


class _dict_proxy(_list_proxy):
    def __init__(self, proxied_dict):
        _list_proxy.__init__(self, proxied_dict)
    def __getattribute__(self, a):
        return j2p(object.__getattribute__(self, 'data').__getitem__(a))
    def __setattr__(self, a, v):
        return object.__getattribute__(self, 'data').__setitem__(a, v)


def p2j(x):
    """p2j gives back the underlying json-ic json-ic nested
    dictionary/list structure of an object or attribute created with
    j2p.
    """
    if isinstance(x, (_list_proxy, _dict_proxy)):
        return object.__getattribute__(x, 'data')
    else:
        return x

现在我想知道是否有一种优雅的方式来映射一整套__*__ 特殊函数,例如__iter____delitem__?所以我不需要使用p2j() 来解包,只是为了迭代或做其他pythonic的东西。

# today:
for i in p2j(aa.y):
     print i
# would like to...
for i in aa.y:
     print i

【问题讨论】:

标签: json metaprogramming python


【解决方案1】:

我认为你让这变得比它需要的更复杂。如果我的理解正确,您需要做的就是:

import json

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

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

    def __delattr__(self, name):
        del self[name]

j = '{"y": [2, 3, {"a": 55, "b": 66}], "x": 4}'

aa = json.loads(j, object_hook=Struct)

for i in aa.y:
    print(i)

当您加载 JSON 时,object_hook 参数允许您指定一个可调用对象来处理它加载的对象。我刚刚用它把字典变成了一个对象,允许属性访问它的键。 Docs

【讨论】:

  • 这是一个有趣的方法。但是,看起来我松散了list()dict() 结构的底层嵌套dict(),甚至从未构造过。我依赖于此。
  • @SusanneOberhauser:我不太清楚你的意思。它将只是 Struct()list()Struct() 代替。 isinstance(aa, dict) 应该仍然可以工作,作为 Struct 的子类 dict,如果需要,您仍然可以使用 aa['y'] 表示法。似乎很容易使代码适应它。
  • 我意识到,如果我将嵌套子结构添加为 Struct 而不是 dict,只要 dict 属性和字典属性。 aa.itemsStruct 的内置方法,但它是 _dict_proxy 字典中的一个键。所以aa.copy = 44 在后者中按预期工作,但在前者中则不然。我想我真的很想了解如何使用 python 元编程将一整套成员函数映射到代理对象。
  • 是的,我故意使用__getattr__ 而不是__getattribute__,因为覆盖默认方法会打开一个丑陋的蠕虫罐。如果这仅适用于 JSON,我要么忍受必须做 aa['items'],要么专门重写一些常用名称以充当键而不是方法。如果您对编程理论更感兴趣,您可能想就此提出一个新问题。
【解决方案2】:

an attrdict library 以非常安全的方式做到了这一点,但如果您愿意,this answer 中提供了一种快速而肮脏(可能泄漏内存)的方法:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

j = '{"y": [2, 3, {"a": 55, "b": 66}], "x": 4}'
aa = json.loads(j, object_hook=AttrDict)

【讨论】:

    【解决方案3】:

    我找到了答案:There is intentionally no way to automatically map the special methods in python, using __getattribute__。所以要实现我想要的,我需要一个接一个地明确定义所有特殊方法,如__len__

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多