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


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.