вложенность с помощью namedtuple - PullRequest
9 голосов
/ 05 декабря 2011

У меня проблемы с получением данных в форме, которую я хотел бы получить на python.

В основном у меня есть программа, которая читает двоичные данные и предоставляет функции для построения и анализа указанных данных.

Мои данные имеют основные заголовки, а затем подзаголовки, которые могут иметь любое количество различных типов данных.

Я хотел бы иметь возможность доступа к моим данным, например:

>>> a = myDatafile.readit()
>>> a.elements.hydrogen.distributionfunction
(a big array)
>>> a.elements.hydrogen.mass
1
>>> a.elements.carbon.mass
12

но я не знаю имен атомов до времени выполнения.

Я попытался использовать namedtuple, например, после того, как прочитал все имена атомов:

self.elements = namedtuple('elements',elementlist)

Где elementlist - это список строк, например («водород», «углерод»).Но проблема в том, что я не могу вложить их, например:

for i in range(0,self.nelements):
    self.elements[i] = namedtuple('details',['ux','uy','uz','mass','distributionfunction'])

, а затем получить доступ к значениям, например,

self.elements.electron.distributionfunction.

Может быть, я делаю это полностьюнеправильно.Я довольно неопытен с питоном.Я знаю, что это было бы легко сделать, если бы меня не беспокоили динамические имена переменных.

Надеюсь, я ясно дал понять, чего пытаюсь достичь!

Ответы [ 4 ]

5 голосов
/ 05 декабря 2011

Не зная ваших данных, мы можем дать только общее решение.

Учитывая, что первые две строки содержат заголовки и подзаголовки, читая их каким-то образом, вы определили иерархию.Все, что вам нужно сделать, это создать иерархический словарь.

Например, расширить ваш пример

data.elements.hydrogen.distributionfunction
data.elements.nitrogen.xyzfunction
data.elements.nitrogen.distributionfunction
data.compound.water.distributionfunction
data.compound.hcl.xyzfunction

Таким образом, мы должны создать словарь как таковой

{'data':{'elements':{'hydrogen':{'distributionfunction':<something>}
                     'nitrogen':{'xyzfunction':<something>,
                           'distributionfunction':<something>}
                }
       compound:{'water':{'distributionfunction':<something>}
                 'hcl':{'xyzfunction':<something>}
                }
       }
 }

как вы будете заполнять словарь, зависит от данных, которые сейчас сложно сказать.Но ключи к словарю вы должны заполнять из заголовков, и каким-то образом вам нужно сопоставить данные с соответствующим значением в пустом слоте словаря.

Как только карта заполнена, вы можете получить к ней доступ как

 yourDict['data']['compound']['hcl']['xyzfunction']
3 голосов
/ 05 декабря 2011

Если ваше имя элемента является динамическим и получено из данных во время выполнения, вы можете назначить их для dict и получить доступ следующим образом

elements['hydrogen'].mass

, но если вам нужна точечная нотация, вы можете создавать атрибуты во время выполнениянапример,

from collections import namedtuple

class Elements(object):
    def add_element(self, elementname, element):
        setattr(self, elementname, element)

Element = namedtuple('Element', ['ux','uy','uz','mass','distributionfunction'])

elements = Elements()
for data in [('hydrogen',1,1,1,1,1), ('helium',2,2,2,2,2), ('carbon',3,3,3,3,3)]:
    elementname = data[0]
    element = Element._make(data[1:])
    elements.add_element(elementname, element)

print elements.hydrogen.mass
print elements.carbon.distributionfunction

Здесь я предполагаю, что у вас есть данные, но с данными в любом другом формате вы можете сделать аналогичные трюки

2 голосов
/ 31 августа 2016

Вот метод для рекурсивного создания именованных кортежей из вложенных данных.

from collections import Mapping, namedtuple


def namedtuplify(mapping, name='NT'):  # thank you https://gist.github.com/hangtwenty/5960435
    """ Convert mappings to namedtuples recursively. """
    if isinstance(mapping, Mapping):
        for key, value in list(mapping.items()):
            mapping[key] = namedtuplify(value)
        return namedtuple_wrapper(name, **mapping)
    elif isinstance(mapping, list):
        return [namedtuplify(item) for item in mapping]
    return mapping

def namedtuple_wrapper(name, **kwargs):
    wrap = namedtuple(name, kwargs)
    return wrap(**kwargs)


stuff = {'data': {'elements': {'hydrogen': {'distributionfunction': 'foo'}, 
  'nitrogen': {'xyzfunction': 'bar', 
    'distributionfunction': 'baz'}
  },
  'compound': {'water': {'distributionfunction': 'lorem'}, 
    'hcl': {'xyzfunction': 'ipsum'}}}
 }

example = namedtuplify(stuff)

example.data.elements.hydrogen.distributionfunction  # 'foo'
0 голосов
/ 17 ноября 2016

У меня была та же проблема с вложенным json, но мне нужно было иметь возможность сериализовать вывод с помощью pickle, который не любит, когда вы создаете объекты на лету.

Я взял ответ @ bren и улучшил еготак что полученная структура будет сериализуемой с помощью pickle.Вы должны сохранить ссылки на каждую структуру, которую вы создаете, на глобальные переменные, чтобы pickle мог следить за ними.

##############################################
class Json2Struct:
    '''
    Convert mappings to nested namedtuples

    Usage:
        jStruct = Json2Struct('JS').json2Struct(json)
    '''
##############################################

    def __init__(self, name):
        self.namePrefix = name
        self.nameSuffix = 0


    def json2Struct(self, jsonObj):  # thank you https://gist.github.com/hangtwenty/5960435
        """ 
        Convert mappings to namedtuples recursively. 
        """
        if isinstance(jsonObj, Mapping):
            for key, value in list(jsonObj.items()):
                jsonObj[key] = self.json2Struct(value)
            return self.namedtuple_wrapper(**jsonObj)
        elif isinstance(jsonObj, list):
            return [self.json2Struct(item) for item in jsonObj]
        return jsonObj


    def namedtuple_wrapper(self, **kwargs):
        self.nameSuffix += 1
        name = self.namePrefix + str(self.nameSuffix)

        Jstruct = namedtuple(name, kwargs)
        globals()[name] = Jstruct

        return Jstruct(**kwargs)

Пример ниже должен работать следующим образом и быть сериализуемым:

stuff = {'data': {'elements': {'hydrogen': {'distributionfunction': 'foo'}, 
  'nitrogen': {'xyzfunction': 'bar', 
    'distributionfunction': 'baz'}
  },
  'compound': {'water': {'distributionfunction': 'lorem'}, 
    'hcl': {'xyzfunction': 'ipsum'}}}
 }

example = Json2Struct('JS').json2Struct(stuff)

example.data.elements.hydrogen.distributionfunction  # 'foo'
...