차이를 설정할 때 마지막 요소를 무시하는 파이썬 방식


11

두 가지가 있다고 가정 해 봅시다 set().

a = {('1', '2', '3', 'a'), ('1', '2', '4', 'a'), ('1', '2', '5', 'b')}
b = {('1', '2', '3', 'b'), ('1', '2', '4', 'b'), ('1', '2', '6', 'b')}

이제 내가하고 싶은 것은 설정된 차이를 찾아 b \ a모든 튜플의 마지막 요소를 무시하는 것입니다. 따라서 다음과 같이하는 것과 같습니다.

a = {('1', '2', '3'), ('1', '2', '4'), ('1', '2', '5')}
b = {('1', '2', '3'), ('1', '2', '4'), ('1', '2', '6')}

In[1]: b - a
Out[1]: {('1', '2', '6')}

예상 출력 :

b \ a = {('1', '2', '6', 'b')}

각 세트를 수동으로 반복하고 각 세트를 검사하지 않고도이를 달성하는 명백한 / pythonic 방법이 tuple[:3]있습니까?


3
내 첫 생각은 클래스를 만들고 비교 연산자를 정의하는 것입니다
Kenny Ostrom

2
하위 클래스를 작성 set하고 차이 연산을 겹쳐 씁니다. 내가 아는 즉시 사용 가능한 솔루션이 없으며 솔루션이 존재하는지 의심합니다.
Ev. Kounis

집합에 대한 "key = ..."또는 이와 유사한 것이 없습니다 (정렬 (..)). 튜플은 변경 불가능하고 해시 가능하며 해시를 기준으로 비교됩니다. 하나의 요소를 제거하면 해시가 무효화됩니다. 따라서 불가능합니다. 값이 필요하지 않은 경우 3 부분 세트를 만들 수 있습니다.aa = { t[:3] for t in a }
Patrick Artner

2
@ AK47 두 세트 S와 T의 (세트) 차이는 S ∖ T로 작성되며 T의 요소가 아닌 S의 요소로 구성된 세트를 의미합니다. x∈S ∖ T⟺x∈S∧x∉T
Grajdeanu Alex.

tuple차이 연산자의 서브 클래스 및 재정의
Pynchia

답변:


10

다음은 튜플의 일반적인 해싱 동작을 재정의하기 위해 자신의 클래스를 작성하는 방법입니다.

a_data = [('1', '2', '3', 'a'), ('1', '2', '4', 'a'), ('1', '2', '5', 'b')]
b_data = [('1', '2', '3', 'b'), ('1', '2', '4', 'b'), ('1', '2', '6', 'b')]

class HashableIgnoresLastElement(tuple):
    def __eq__(self, other):
        return self[:-1] == other[:-1]

    def __hash__(self):
        return hash(self[:-1])

a = set(map(HashableIgnoresLastElement, a_data))
b = set(map(HashableIgnoresLastElement, b_data))

print(b - a)

출력

{('1', '2', '6', 'b')}

튜플 세트의 동작 방식을 수정하려면 튜플의 해시 방식을 수정해야합니다.

에서 여기 ,

객체는 수명 동안 변경되지 않는 ( __hash__()메소드가 필요함 ) 해시 값이 있고 다른 객체 ( __eq__()메소드가 필요함)와 비교할 수있는 경우 해시 가능합니다 . 동일하게 비교하는 해시 가능 객체는 동일한 해시 값을 가져야합니다.

해시 기능을 사용하면 객체가 사전 키 및 집합 멤버로 사용할 수 있습니다. 이러한 데이터 구조는 해시 값을 내부적으로 사용하기 때문입니다.

따라서 해싱이 마지막 요소를 무시 하게 하려면 dunder 메소드 __eq____hash__적절하게 오버로드해야합니다 . 우리가해야 할 일은 마지막 요소를 잘라낸 다음 normal의 적절한 메소드에 위임하기 때문에 그렇게 힘들지는 않습니다 tuple.

더 읽을 거리 :


1
매우 깔끔합니다! 이것이 어떻게 작동하는지 조금 설명해 주시겠습니까? 이 솔루션을 읽는 사람들에게는 가치가 있습니다.
Grajdeanu Alex.

G 간단한 설명을 추가했습니다 :). 실제로 그것은 비트와 연산자 오버로드 조각과 파이썬에서 해싱이 어떻게 작동하는지 결합합니다.
Izaak van Dongen

2

가장 단순한 솔루션이 색인을 내포하는 것처럼 보이기 때문에 집합이 아닌 목록을 정의 a하고 b사용 하는 한 가지 방법이 있습니다 b.

a = [('1', '2', '3', 'a'), ('1', '2', '4', 'a'), ('1', '2', '5', 'b')]
b = [('1', '2', '3', 'b'), ('1', '2', '4', 'b'), ('1', '2', '6', 'b')]

# reconstruct the sets of tuples removing the last elements
a_ = {tuple(t) for *t, _ in a}
b_ = [tuple(t) for *t, _ in b]

# index b based on whether an element in a_
[b[ix] for ix, j in enumerate(b_) if j not in a_]
# [('1', '2', '6', 'b')]

1
내가 잘못 찾은 경우 O (n)입니다. 검색에 세트를 사용하기 때문입니다. 나는 Izaak 반 Dongen의 대답은 훨씬 더 우아한 @konrad 생각 할 수 있지만
yatu

1
당신은 전적으로 옳습니다. 목록을 사용하고 열거했습니다. 그러나 세트 차이도 첫 세트를 반복해야합니다.
Konrad Rudolph

1

잘 작동합니다. 제대로 작동하지 않는 것은 당신의 데이터입니다. 서로 다르게 보이지만 실제로 동일한 경우 원하는 것처럼 작동하는 데이터 유형을 정의하십시오. 그런 다음 자체적으로 큰 효과를 발휘합니다.

class thing:
    def __init__(self, a, b, c, d):
        self.a, self.b, self.c, self.d = a, b, c, d

    def __repr__(self):
        return (str((self.a, self.b, self.c, self.d)))

    def __hash__(self):
        return hash((self.a, self.b, self.c))

    def __eq__(self, other):
        return self.a == other.a and self.b == other.b and self.c == other.c       

a = {thing('1', '2', '3', 'a'), thing('1', '2', '4', 'a'), thing('1', '2', '5', 'b')}
b = {thing('1', '2', '3', 'b'), thing('1', '2', '4', 'b'), thing('1', '2', '6', 'b')}
print (b - a)

{( '1', '2', '6', 'b')}


3
튜플 __repr__과 관련 __hash__하여 정의 했지만 __eq__. 여기에서 튜플을 사용하는 것이 더 짧지 않습니까? 실제로 슬라이싱을 사용 __hash__하여 코드를 더 짧게 할 수 있습니다 .
Konrad Rudolph

예, 단지 튜플을 서브 클래 싱하는 것이 질문에 따라 크게 개선되었습니다.
Kenny Ostrom
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.