2012-04-05 14 views
8

estoy trabajando con estructuras de datos JSON-anidadas como en Python 2.7 que intercambio con algún código Perl extranjera. Solo quiero 'trabajar con' estas estructuras anidadas de listas y diccionarios de una manera amoretónica.Utilizando las teclas de JSON como atributos en JSON anidada

Así que si tengo una estructura como esta ...

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

... yo quiero ser capaz de tratar con él en un script en Python, como si se anida clases Python/estructuras, como esto :

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

Por lo tanto, aa es un proxy en la estructura original. Esto es lo que se me ocurrió hasta ahora, inspirado en la excelente pregunta 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 

Ahora me pregunto si hay una manera elegante de mapear un conjunto de los __*__ funciones especiales, como __iter__, __delitem__? así que no necesito desenvolver cosas usando p2j() solo para iterar o hacer otras cosas pitónicas.

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

creo que busca este solución - http://stackoverflow.com/questions/4984647/accessing-dict-keys-like-an-attribute-in-python#answer-14620633 – Yurik

Respuesta

3

Hay an attrdict library que hace exactamente eso de una manera muy segura, pero si lo desea, un enfoque rápido y sucio (memoria que posiblemente se fuga) se dio en 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) 
11

Creo que estás haciendo esto más complejo de lo necesario. Si he entendido bien, todo lo que necesitará hacer es esto:

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) 

Al cargar JSON, el parámetro object_hook le permite especificar un objeto exigible para procesar objetos que se cargue. Acabo de usarlo para convertir el dict en un objeto que permite el acceso de atributos a sus claves. Docs

+0

Este es un enfoque interesante. Sin embargo, parece que pierdo el '' dict() 'anidado subyacente de' list() 'de la estructura' dict() ', nunca se construye. Y dependo de eso. –

+0

@SusanneOberhauser: No estoy muy seguro de lo que quieres decir. Simplemente será 'Struct()' de 'list()' de 'Struct()' en su lugar. 'isinstance (aa, dict)' debería funcionar, como Struct subclases dict, y aún puedes usar la notación 'aa ['y']' si la necesitas. Parece que sería fácil adaptar el código a eso. –

+0

Me di cuenta de que si agrego subestructuras anidadas como 'Struct' en lugar de' dict', me acerco a lo que pretendo, siempre y cuando no haya conflictos de nombres entre los atributos 'dict' y los atributos del diccionario. 'aa.items' es un método integrado para' Struct', pero es una clave en el dict de '_dict_proxy'. entonces 'aa.copy = 44' funciona como está previsto en este último, pero no en el primero. creo que realmente me gustaría entender cómo asignar un conjunto de funciones de miembro de los objetos proxy utilizando la programación meta pitón. –

Cuestiones relacionadas