Python:TypeError:unhashable type: 'list'


94

次のようなファイルを取得しようとしています

AAA x 111
AAB x 111
AAA x 112
AAC x 123
...

そして、出力が次のようになるように辞書を使用します

{AAA: ['111', '112'], AAB: ['111'], AAC: [123], ...}

これは私が試したものです

file = open("filename.txt", "r") 
readline = file.readline().rstrip()
while readline!= "":
    list = []
    list = readline.split(" ")
    j = list.index("x")
    k = list[0:j]
    v = list[j + 1:]
    d = {}
    if k not in d == False:
        d[k] = []
    d[k].append(v)
    readline = file.readline().rstrip()

を取得し続けTypeError: unhashable type: 'list'ます。辞書のキーはリストにできないことは知っていますが、私の値をキーではなくリストにしようとしています。どこかで間違えたのかな。

回答:


56

他の回答で示されているように、エラーはによるものk = list[0:j]で、キーがリストに変換されます。あなたが試すことができる1つのことは、split関数を利用するようにコードを作り直すことです:

# Using with ensures that the file is properly closed when you're done
with open('filename.txt', 'rb') as f:
  d = {}
  # Here we use readlines() to split the file into a list where each element is a line
  for line in f.readlines():
    # Now we split the file on `x`, since the part before the x will be
    # the key and the part after the value
    line = line.split('x')
    # Take the line parts and strip out the spaces, assigning them to the variables
    # Once you get a bit more comfortable, this works as well:
    # key, value = [x.strip() for x in line] 
    key = line[0].strip()
    value = line[1].strip()
    # Now we check if the dictionary contains the key; if so, append the new value,
    # and if not, make a new list that contains the current value
    # (For future reference, this is a great place for a defaultdict :)
    if key in d:
      d[key].append(value)
    else:
      d[key] = [value]

print d
# {'AAA': ['111', '112'], 'AAC': ['123'], 'AAB': ['111']}

Python 3.xを使用している場合は、Python 3.xが正しく機能するように少し調整する必要があります。でファイルを開くrb場合は、使用する必要がありますline = line.split(b'x')(これにより、バイトを適切なタイプの文字列で分割していることを確認できます)。with open('filename.txt', 'rU') as f:(またはwith open('filename.txt', 'r') as f:)を使用してファイルを開くこともでき、正常に動作するはずです。


私はこれを試してTypeErrorを受け取りました:タイプstrは行 "line = line.split( 'x')"のバッファAPIをサポートしていません
Keenan

1
@ user1871081ああ、あなたはPython 3.xを使っていますか?これで動作するアップデートを投稿します。
RocketDonkey

31

注: この回答は、尋ねられた質問に明示的に回答するものではありません。他の答えはそれを行います。質問はシナリオに固有であり、発生する例外は一般的であるため、この回答は一般的なケースを示しています。

ハッシュ値は単なる整数であり、辞書ルックアップ中に辞書キーをすばやく比較するために使用されます。

内部的には、hash()メソッドは__hash__()デフォルトで任意のオブジェクトに設定されているオブジェクトのメソッドを呼び出します。

ネストされたリストをセットに変換する

>>> a = [1,2,3,4,[5,6,7],8,9]
>>> set(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

これは、ハッシュできないリストであるリスト内のリストが原因で発生します。これは、内部のネストされたリストをタプル変換することで解決できます。

>>> set([1, 2, 3, 4, (5, 6, 7), 8, 9])
set([1, 2, 3, 4, 8, 9, (5, 6, 7)])

ネストされたリストを明示的にハッシュする

>>> hash([1, 2, 3, [4, 5,], 6, 7])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'


>>> hash(tuple([1, 2, 3, [4, 5,], 6, 7]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> hash(tuple([1, 2, 3, tuple([4, 5,]), 6, 7]))
-7943504827826258506

このエラーを回避する解決策は、リストではなくネストされたタプルを持つようにリストを再構成することです。


4
リストが大きすぎる場合はどうなりますか?それは良い解決策ですが、一般的ではありません
msh855

1
@ msh855サイズ制限はありますか?サイズ100,000のタプルで辞書をテストしましたが、うまく機能しました(python 3.6を使用しています)
Sreram

18

k(リストである)をのキーとして使用しようとしていますd。リストは変更可能で、dictキーとして使用できません。

また、次の行があるため、辞書のリストを初期化することはありません。

if k not in d == False:

どちらにする必要があります:

if k not in d == True:

実際には次のようになります。

if k not in d:

5

unhashable type: 'list'例外が発生するのは、k = list[0:j]セットkがリストの「スライス」になるためです。これは、論理的には別の、多くの場合は短いリストです。必要なのは、リストの最初のアイテムだけを取得することk = list[0]です。同じv = list[j + 1:]ことv = list[2]は、への呼び出しから返されたリストの3番目の要素に対してのみreadline.split(" ")です。

他にもいくつかコードに問題がある可能性があることに気づきました。重要なのは、ループで読み取られた各行ごとに(再)初期化dしたくない場合ですd = {}。もう1つは、必要に応じていずれかの組み込み型にアクセスできないようにするため、変数に組み込み型のいずれかと同じ名前を付けることは一般に良い考えではなく、慣れている他のユーザーを混乱させるこれらの標準アイテムの1つを示す名前。そのため、そのlistような問題を回避するには、変数変数の名前を別の名前に変更する必要があります。

これらの変更を加えた作業バージョンは次のifとおりです。また、キーが既に辞書にあるかどうかを確認するステートメント式を簡略化しました。このようなことを行う短い暗黙の方法がありますが、条件付きステートメントは今のところ大丈夫です。

d = {}
file = open("filename.txt", "r")
readline = file.readline().rstrip()
while readline:
    lst = readline.split(" ") # Split into sequence like ['AAA', 'x', '111'].
    k = lst[0]  # First item.
    v = lst[2]  # Third item.
    if k not in d:  # New key?
        d[k] = []  # Initialize its associated value to an empty list.
    d[k].append(v)
    readline = file.readline().rstrip()

file.close()  # Done reading file.
print('d: {}'.format(d))

出力:

d: {'AAA': ['111', '112'], 'AAC': ['123'], 'AAB': ['111']}

0

これTypeErrork、がリストであるため、行で別のリストのスライスを使用して作成されているために発生していますk = list[0:j]。これはおそらくのようになるはずなk = ' '.join(list[0:j])ので、代わりに文字列を使用します。

これに加えて、あなたのifジェシーの答え、読むべきで述べたようにステートメントが間違っているif k not in dif not k in d(私は後者を好みます)。

またd = {}forループの内側にいるため、反復ごとに辞書をクリアしています。

ビルトインをマスキングするため、変数名として、listまたはfile変数名として使用しないでください。

これが私があなたのコードを書き直す方法です:

d = {}
with open("filename.txt", "r") as input_file:
    for line in input_file:
        fields = line.split()
        j = fields.index("x")
        k = " ".join(fields[:j])
        d.setdefault(k, []).append(" ".join(fields[j+1:]))

上記のdict.setdefault()メソッドは、if k not in dコードのロジックを置き換えます。


好みはあなたの完全な権利ですnot k in dが、初心者を(not k) in dと混同する可能性がありますk not in dが、あいまいさはありません
Jesse the Game

演算子not inとしてリストされている「pythonic」の方法であるとさえ主張します。
Jesse the Game

ええ、私の好みはおそらく他の言語を最初に学ぶことから来ると思います。封じ込めテストのようなものでは、これのための演算子がないので、あなたはのようなことをします!a.contains(b)not inもっとpythonicかもしれませんが、ブール式で逆を使用するよりも、2つの単語演算子の概念がより混乱していることに気づきます。
Andrew Clark

-1
    python 3.2

    with open("d://test.txt") as f:
              k=(((i.split("\n"))[0].rstrip()).split() for i in f.readlines())
              d={}
              for i,_,v in k:
                      d.setdefault(i,[]).append(v)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.