web-dev-qa-db-ja.com

Python:TypeError:ハッシュできない型: 'list'

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

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'を取得し続けます。辞書のキーをリストにすることはできませんが、キーではなくリストに値を入れようとしています。どこかで間違えたのではないかと思っています。

60
Keenan

他の回答で示されているように、エラーは、キーがリストに変換される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を使用している場合、正しく動作させるために微調整を行う必要があることに注意してください。 rbを使用してファイルを開く場合は、line = line.split(b'x')を使用する必要があります(これにより、適切なタイプの文字列でバイトを確実に分割できます)。また、with open('filename.txt', 'rU') as f:(またはwith open('filename.txt', 'r') as f:)を使用してファイルを開くこともできます。

39
RocketDonkey

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

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

if k not in d == False:

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

if k not in d == True:

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

if k not in d:
14
Jesse the Game

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

ハッシュ値は、ディクショナリのルックアップ中にディクショナリキーをすばやく比較するために使用される単なる整数です。

内部的に、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'

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

>>> 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

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

10

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

コードに関する他のいくつかの問題に気づきましたが、そのうちのいくつかについて言及します。大きな問題は、ループで読み取られる各行に対してdd = {}で(再)初期化したくないことです。もう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']}
4
martineau

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

これに加えて、ifステートメントは、if k not in dまたはif not k in dを読み取る必要があるJesseの回答で指摘されているように正しくありません(後者をお勧めします)。

また、forループ内にd = {}があるため、各反復で辞書をクリアしています。

ビルトインをマスクするため、変数名として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ロジックを置き換えます。

0
Andrew Clark