0

リスト (たとえば、本) の各項目に対して API を呼び出して、本に関するメタデータを取得する作業プログラムがあります。book : メタデータを dict に保存して使用します。これにより、メタデータの収集中にユーザーが待機するため、過剰な呼び出しを避けるために、必要な場合にのみ応答を取得できるように、前述の API 呼び出しを行う前に dict を CSV に永続化し、それをロードしています。

ただし、永続化された dict を読み取るコンテキスト マネージャーを導入し、関数 ("gatherfiles()") に call-if-not-there ロジックを実行すると、3 番目の関数にアクセスできなくなります。

メイン関数を呼び出すと、gatherfiles() によって dict が返されることがわかりますが、3 番目の関数呼び出し (「pickabook()」) を行うと、keyerror が発生し、空の辞書が表示されます。

以下のコードの編集版を入れました。私の推測では、何らかの形でコンテキスト マネージャーがスコープを変更した (つまり、1 つの shimdict をグローバルとして、もう 1 つをローカルとして扱う) ことですが、私がオンラインで読むことができるものを考えると、それは正しくないようです。それで、醜くない考えはありますか?


shimdict = {}

def pickabook(book=None):

        print(shimdict, "<-this is {}. why?!?")
        picked = shimdict.pop(book)

def gatherfiles(directory):

    with open('test.csv', 'rb') as f:
      reader = csv.reader(f,)
      shimdict = dict((rows[0],rows[1]) for rows in reader)

    with open('test.csv', 'a+b') as f:  
      w = csv.writer(f)

      ff = os.listdir(directory)
      for f in ff:
          if f.rsplit('.', 1)[1].lower() in [....]:
                filename = os.path.join(directory, f)

                if filename in shimdict.keys():
                    print("already here")

                else:

                    print("make the api call, then write the value to dict & then csv")

                    shimdict[filename] = (returnedvalue)

                    w.writerow([filename, (returnedvalue)])

    return shimdict

def main():

    shimdict = gatherfiles(directory)
    print(shimdict, "<-dictionary works")


    while 1:
        print(shimdict, "<-dictionary works")
        current = pickabook(bookname)

---- 以下を編集 ---- 私は自分の質問を明確に十分に提起したとは思いません。コンテキストマネージャーが削除されている場合、「pickabook()」の辞書「shimdict」にアクセスできます。つまり、次のコードを使用します。

def gatherfiles(directory):

    ff = os.listdir(directory)
    for f in ff:
        if f.rsplit('.', 1)[1].lower() in [....]:
            filename = os.path.join(directory, f)

            shimdict[filename] = (returnedvalue)

return shimdict        

したがって、グローバルを使用するか、ローカル辞書を関数に渡してこれを修正できることを完全に理解していますが、コンテキストマネージャーを追加すると動作が変わる理由を知りたいです。

4

1 に答える 1

0

Waleed Khan がコメントしたように、問題は関数と関数のshimdict変数がグローバル変数と同じではないことです。後者は、モジュールの上部で空の辞書として初期化され、空のままです。そこから飛び出そうとするものです。他のものは、関数内のローカル変数です (この場合、たまたま同じオブジェクトを参照しますが、他の関数内の同じ名前を持つローカル変数の場合は必ずしもそうとは限りません)。orステートメントを使用して別の方法で実行するように指示しない限り、Python は、新しい名前を割り当てるときに、デフォルトで常にローカル変数を使用します。maingatherfilesshimdictpickabookglobalnonlocal

この特定の状況では、2 つの方法のいずれかで関数を正しく動作させることができます。または のどちらかglobal shimdictの先頭に置くことができます(後者の場合は、後でグローバル名でアクセスされるため、辞書をスキップできます)。maingatherfilesreturn

ただし、より良い解決策は、おそらくグローバル変数を完全に取り除き、mainそのローカルshimdictを に渡すことpickabookです。pickabook関数宣言を次のように変更するだけです。

def pickabook(shimdict, book=None):

そして、からへの呼び出しmain

current = pickabook(shmdict, bookname)

編集して質問に答える編集:

コンテキスト マネージャーは重要ではありません。編集で表示する作業コードと質問の元の部分の非作業コードの実際の違いは、後者では name への割り当てを行っていることですshimdict

shimdict = dict((rows[0],rows[1]) for rows in reader)

前者では、辞書の個々の項目にのみ割り当てます。

shimdict[filename] = (returnedvalue)

前者は、関数内に新しいローカル ディクショナリを作成します (globalステートメントを使用しない場合)。後者は絶対にアクセスせず、常に のグローバル バージョンにアクセスしshimdictます。

したがって、別の解決策は、ジェネレーター式で全体を作成するのではなく、コードを使用してコンテキストマネージャーを書き直して、各項目を辞書に個別に割り当てることだと思います。

with open('test.csv', 'rb') as f:
    reader = csv.reader(f,)
    for row in reader:
        shimdict[row[0]] = row[1]

グローバル変数を避けることをお勧めします。これは、修正が困難なバグを伴う複雑なコードにつながることが多いためです。

于 2014-05-14T05:16:23.697 に答える