Python : TypeError : 해시 할 수없는 유형 : 'list'


95

다음과 같은 파일을 가져 오려고합니다.

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]키가 목록으로 변환되는 으로 인한 것 입니다. 시도해 볼 수있는 한 가지는 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:)을 사용하여 파일을 열 수도 있으며 제대로 작동합니다.


나는 이것을 시도하고 나는 TypeError를 얻는다 : type str은 줄 "line = line.split ( 'x')"에서 버퍼 API를 지원하지 않는다
Keenan

1
@ user1871081 아, 파이썬 3.x를 사용하고 있습니까? 나는 그것과 함께 작동하는 업데이트를 게시 할 것입니다.
RocketDonkey

31

참고 : 이 답변은 질문에 명시 적으로 답변하지 않습니다. 다른 답변은 그것을합니다. 질문은 시나리오 와 관련이 있고 발생한 예외는 general 이므로이 답변은 일반적인 경우를 가리 킵니다.

해시 값은 사전 조회 중에 사전 키를 빠르게 비교하는 데 사용되는 정수일뿐입니다.

내부적으로 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
목록이 너무 크면 어떻게합니까? 좋은 해결책이지만 충분히 일반적이지 않은 것
같습니다

1
@ msh855 크기 제한이 있습니까? 나는 100,000 크기의 튜플로 사전을 테스트했고 그것은 나에게 잘 작동했다 (나는 파이썬 3.6을 사용하고있다)
Sreram

18

k(목록)을 키로 사용하려고합니다 d. 목록은 변경 가능하며 사전 키로 사용할 수 없습니다.

또한 다음 줄 때문에 사전에있는 목록을 초기화하지 않습니다.

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]합니다 readline.split(" ").

코드에서 몇 가지 다른 문제를 발견했으며 그중 몇 가지를 언급하겠습니다. 큰 하나는 (재) 원하는 초기화하지 않는 것입니다 d함께 d = {}각 라인은 루프에서 읽기. 다른 하나는 일반적으로 기본 제공 유형과 동일한 이름을 변수에 지정하는 것은 좋지 않다는 것입니다. 필요한 경우 변수 중 하나에 액세스 할 수 없게되므로 이러한 표준 항목 중 하나를 지정하는 이름. 따라서 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

TypeError때문에 일어나고 k이 광고와 다른리스트에서 슬라이스를 이용하여 생성되기 때문에, 목록이다 k = list[0:j]. 이것은 아마도와 같을 k = ' '.join(list[0:j])것이므로 대신 문자열이 있습니다.

이 외에도 ifJesse의 답변에서 언급했듯이 귀하의 진술은 if k not in d또는 if 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모호함이 없습니다
제시 게임

나는 심지어 연산자not in 로 나열되는 '파이썬 방식'이라고 주장합니다 .
Jesse the Game

예, 제 선호는 아마도 다른 언어를 먼저 배우는 것에서 비롯된 것 같습니다. 봉쇄 테스트와 같은 경우에는 운영자가 없으므로 !a.contains(b). not in부울 식에 역을 사용하는 것보다 두 단어 연산자의 개념이 더 혼란 스러울 수 있습니다.
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.