2016-05-21 2 views
0

Я создаю вложенный словарь в python, содержащий записи для файлов, хранящихся в ведро Amazon S3. Так что если в моем ведре у меня есть несколько файлов, как это:Лучшая практика для рекурсивного обновления вложенного словаря?

mys3bucket/подкаталог/world.txt

mys3bucket/подкаталог/hello.txt

mys3bucket/foobar.txt

I хотите сделать словарь в питоне в этом формате:

dict = { 'subdir' : { 'world.txt' : 'file', 'hello.txt' : 'file' }, 'foobar.txt' :'file' } 

значение («файл») не имеет никакого значения в этом контексте, но они могут быть заменены с размером файла или с omething else (для этого вопроса это не имеет значения). Дело в том, что словарь должен быть вложен из-за подкаталогов, и, очевидно, уровень вложенности зависит от того, насколько глубоким является конкретное дерево. Я написал рабочую реализацию, которая уже делает это:

#!/usr/bin/python 
import httplib 
from re import compile as recomp 

pattern = recomp("<Key>(.*?)<\/Key>") 

def main(bucketname='elasticmapreduce'): 
    url = bucketname + '.s3.amazonaws.com' 
    HTTPconnection = httplib.HTTPConnection(url) 
    HTTPconnection.request("GET", "/") 
    response = HTTPconnection.getresponse() 
    content = response.read() 
    fileslist = pattern.findall(content) 

    filesdict = {} 

    def intoDict(path,mydict): 
     if len(path) == 1: 
      mydict[path[0]] = 'file' 
     return mydict 
     else: 
      name = path.pop(0) 
     if name in mydict: 
      mydict[name] = intoDict(path,mydict[name]) 
     else: 
      mydict[name] = intoDict(path,{}) 
     return mydict 

    for line in fileslist: 
     splitline = line.split('/') 
     if splitline[-1] != '': 
      filesdict = intoDict(splitline,filesdict) 

     return filesdict 

имя ведра по умолчанию просто установить в общественном ведро, которое я нашел, чтобы быть в состоянии проверить код на.

Причина для регулярного выражения заключается в том, что S3 возвращает текст, отформатированный в формате XML, когда вы запрашиваете ведро, поэтому регулярное выражение просто извлекает из него пути к файлам.

Меня интересует эффективность моей реализации. Как вы можете видеть в цикле for, я передаю весь словарь каждый раз функции intoDict() и переписываю его, когда он возвращается. Функция intoDict() является рекурсивной/саморегуляционной, что и происходит в гнездовании. Трудно объяснить, что происходит, но я думаю, что вы можете это понять. Мне потребовалось некоторое время, чтобы прийти к этому решению, потому что сначала я пытался использовать dictionary.update() для обновления словаря внутри цикла for, но он работал неправильно.

Мне было интересно, может ли кто-нибудь, кто имеет опыт в вложенных словарях и/или рекурсивных функциях, прокомментировать, является ли это правильным способом достижения того, что я пытаюсь, или можно ли это сделать лучше.

ответ

0

Есть пара вещей, которые можно улучшить. Вместо использования рекурсии вы можете использовать итерацию, чтобы найти правильный путь для вставки. Вы также должны рассмотреть возможность удаления мутации списка здесь: name = path.pop(0). Вам также не нужно присваивать значения filesdict на каждом уровне, если они уже существуют так, как вы определили.

Вот пример использования defaultdict о том, как поставить выше на практике:

from collections import defaultdict 

# Create dict that automatically assigns empty dict to a key that doesn't exist 
dd = lambda: defaultdict(dd) 
filesdict = dd() 

for line in fileslist: 
    path = line.split('/') 
    if path[-1] != '': 
     d = filesdict 

     # Iterate to location where file is to be added without mutating path 
     for i in range(len(path) - 1): 
      # If d[path[i]] doesn't exist empty dict is automatically created here 
      d = d[path[i]] 
     d[path[-1]] = 'file' 

Вы также могли бы рассмотреть возможность использования re.finditer вместо re.findall, так как вы обрабатывать файлы один за другим.

+0

Спасибо @niemmi, это действительно круто! Вы представили мне пару новых концепций. Я изменил код для работы без 'defaultdict', поэтому я добавил: ' if path [i] не в d: d [путь [i]] = {} ' Что-то я не понимаю: когда мы делаем assign 'd = filesdict' Я предполагаю, что Python создает какую-то динамическую связь между двумя словарями? Извиняюсь, если это глупый вопрос. –

+0

@KirillVourlakidis Не глупый вопрос.Я не уверен, что вы подразумеваете под динамической ассоциацией, но это всего лишь две ссылки на один и тот же объект. Теперь после выполнения строки 'd = d [путь [i]]' '' d' относится к дочернему 'dict'. Когда следующий путь обрабатывается, нам нужно начинать с root снова, поэтому нам нужно сохранить ссылку там ('filesdict'). – niemmi

+0

Это имеет смысл в отношении ссылки на один и тот же объект! В моей голове я представляю себе, что Python создает копию всего словаря и связывает их вместе, в результате чего, если кто-то изменен, он автоматически меняет другой, таким образом, мой комментарий «динамической ассоциации». Как вы можете видеть, я все еще неравнодушен к тому, чтобы обойти всю объектно-ориентированную вещь. –