사전 키로서의 커스텀 타입의 객체


185

파이썬 딕셔너리에서 커스텀 타입의 객체를 키로 사용하려면 어떻게해야합니까 ( "object id"를 키로 사용하지 않기를 원합니다)

class MyThing:
    def __init__(self,name,location,length):
            self.name = name
            self.location = location
            self.length = length

이름과 위치가 같은 경우 MyThing을 동일한 키로 사용하고 싶습니다. C # / Java에서 equals 및 hashcode 메서드를 재정의하고 제공해야하며 해시 코드가 의존하는 것을 변경하지 않도록 약속했습니다.

이것을 달성하기 위해 파이썬에서 무엇을해야합니까? 나도해야합니까?

(단순한 경우, 여기에서와 같이 (이름, 위치) 튜플을 키로 배치하는 것이 좋습니다. 그러나 키를 객체로 만들고 싶습니다.)


해시 사용에있어 무엇이 문제입니까?
Rafe Kettler

5
그는이 원하는 아마 때문에 MyThing, 그들은 같은 경우 namelocation인덱스가 서로 다른 두 가지 "객체"별도로 생성 된 경우에도, 같은 값을 반환 할 수있는 사전을.
산타

1
"아마도 (이름, 위치) 튜플을 키로 배치하는 것이 좋습니다. 그러나 키가 객체가되기를 원합니다)"의미 : NON-COMPOSITE 객체?
eyquem

답변:


220

당신은 추가해야 할 2 가지 방법 ,주의 __hash____eq__:

class MyThing:
    def __init__(self,name,location,length):
        self.name = name
        self.location = location
        self.length = length

    def __hash__(self):
        return hash((self.name, self.location))

    def __eq__(self, other):
        return (self.name, self.location) == (other.name, other.location)

    def __ne__(self, other):
        # Not strictly necessary, but to avoid having both x==y and x!=y
        # True at the same time
        return not(self == other)

Python dict 문서 는 주요 객체에 대한 이러한 요구 사항을 정의합니다. 즉, 해시 가능 해야합니다 .


17
hash(self.name)보다보기 좋으며 XOR 링을 피하기 위해 self.name.__hash__()할 수 있습니다 hash((x, y)).
Rosh Oxymoron

5
추가 참고로, 난 그냥 호출 발견 x.__hash__()그렇게하는 것은 또한 잘못 이 있기 때문에, 생산 잘못된 : 결과를 pastebin.com/C9fSH7eF
로시 모순 어법

@Rosh Oxymoron : 의견 감사합니다. 쓸 때 나는 명시 적으로 사용했다 and에 대한 __eq__생각 다음 그러나 "튜플을 사용하지 않는 이유는 무엇입니까?" 어쨌든 종종 그렇게하기 때문에 (더 읽기 쉽다고 생각합니다). 이상한 이유로 내 눈은 __hash__그러나 다시 질문으로 돌아 가지 않았다 .
6502

1
@ user877329 : 블렌더 데이터 구조를 키로 사용하려고합니까? 분명히 일부 저장소에서 특정 객체는 변경을 피하기 위해 먼저 "고정"해야합니다 (파이썬 사전에서 키로 사용 된 값 기반 객체의 변경은 허용되지 않습니다)
6502

1
@ kawing-chiu pythonfiddle.com/eq-method-needs-ne-method <-이것은 Python 2의 "버그"를 보여줍니다. Python 3에는이 문제가 없습니다 . 기본적 __ne__()으로 "고정" 되었습니다 .
Bob Stein

34

Python 2.6 이상의 대안은 사용 collections.namedtuple()하는 것입니다. 특별한 방법을 쓰지 않아도됩니다.

from collections import namedtuple
MyThingBase = namedtuple("MyThingBase", ["name", "location"])
class MyThing(MyThingBase):
    def __new__(cls, name, location, length):
        obj = MyThingBase.__new__(cls, name, location)
        obj.length = length
        return obj

a = MyThing("a", "here", 10)
b = MyThing("a", "here", 20)
c = MyThing("c", "there", 10)
a == b
# True
hash(a) == hash(b)
# True
a == c
# False

20

__hash__특수 해시 시맨틱을 원하면 재정의 __cmp__하거나__eq__ 클래스를 키로 사용할 수 있도록하려면 하십시오. 동등하게 비교하는 객체는 동일한 해시 값을 가져야합니다.

파이썬 __hash__은 정수를 반환 할 것으로 예상 하지만 반환은 Banana()권장하지 않습니다 :)

사용자 정의 클래스는 __hash__기본적으로 id(self)언급 한 것처럼 을 호출합니다 .

설명서의 추가 팁이 있습니다 .

상속 클래스 __hash__() 의 부모 클래스의 메소드를하지만의 의미를 변경 __cmp__()또는 __eq__() 반환 된 해시 값이 더 이상 적합하지 않도록 (예를 들어, 대신 기본 ID를 기반으로 평등 평등의 가치를 기반으로 개념을 전환하여)로 플래그를 명시 적으로 자신을 수 __hash__ = None 클래스 정의에서 설정 하여 해싱 할 수 없습니다 . 그렇게하면 프로그램이 해시 값을 검색하려고 할 때 클래스의 인스턴스가 적절한 TypeError를 발생시킬뿐만 아니라 검사 할 때 해시 할 수없는 것으로 올바르게 식별됩니다 isinstance(obj, collections.Hashable) ( __hash__()TypeError를 명시 적으로 발생 시키는 클래스와 달리 ).


2
혼자 해시는 오버라이드 (override)를 추가하면 하나 필요, 충분하지 않다 __eq__거나 __cmp__.
Oben Sonne

@Oben Sonne : __cmp__파이썬이 사용자 정의 클래스 인 경우 Python에서 제공하지만 새로운 의미를 수용하기 위해 어쨌든 재정의하려고합니다.
Skurmedel

1
@Skurmedel : 예. 그러나 이러한 메서드를 재정의하지 않는 사용자 클래스에서 호출 cmp하여 사용할 수 있지만 =그 중 하나는 비슷한 이름과 위치를 가진 인스턴스가 동일한 사전 키를 가져야하는 질문자의 요구 사항을 충족하도록 구현되어야합니다.
Oben Sonne
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.