2016-05-21 19 views
0

私はpythonでAmazon S3バケットに格納されたファイルのエントリを含むネストされた辞書を作成しています。だから私のバケツに私はこのようなファイルをいくつ持っている場合:ネストされた辞書を再帰的に更新するベストプラクティス?

mys3bucket /サブディレクトリ/ world.txt

mys3bucket /サブディレクトリ/ hello.txt

mys3bucket/foobar.txt

をIこの形式でのpythonで辞書を作りたい:

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

値(「ファイル」)は、この文脈では意味を持たないが、彼らは、ファイルのサイズまたはSで置換することができます何か他のもの(この質問のために重要ではない)。要点は、サブディレクトリのために辞書がネストされなければならないことであり、明らかにネストのレベルは特定のツリーの深さによって異なります。デフォルトのバケット名はちょうど私が上でコードをテストできるようにした公共バケットに設定されている

#!/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.findallre.finditerの使用を検討できます。

+0

ありがとう@niemmi、これは本当にクールです! 新しい概念をいくつか紹介しました。''もしpath [i]がd:d [path [i]] = {} 'にないならば、私は' defaultdict'なしで動作するようにコードを変更した。 私が理解できない何か:代入 'd = filesdict'私は、Pythonが2つの辞書間にある種の動的な関連付けを作成すると思いますか?それが愚かな質問であれば、お詫び申し上げます。 –

+0

@KirillVourlakidisまったく愚かな質問ではありません。私はあなたがダイナミックな関連付けによって何を意味するのかは分かりませんが、同じオブジェクトへの参照は2つだけです。 'd = d [path [i]]'行が実行された後、 'd'は子の' dict'を参照します。次のパスが処理されると、そこから参照を維持する必要があるので、ルートから再度開始する必要があります( 'filesdict')。 – niemmi

+0

同じオブジェクトの参照については意味があります。私の頭の中で、私はPythonが辞書全体のコピーを作成し、それらをリンクすることを想像していました。それが変更されると自動的に他のもの、つまり私の「動的な関連」のコメントが変更されます。 私はまだオブジェクト指向のもの全体に頭を浮かべています。 –

関連する問題