Cách Pythonic bỏ qua phần tử cuối cùng khi thực hiện chênh lệch thiết lập


11

Hãy nói rằng tôi có hai set()s:

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ây giờ, những gì tôi muốn làm là tìm sự khác biệt được thiết lập b \ anhưng bỏ qua phần tử cuối cùng từ mỗi bộ dữ liệu. Vì vậy, nó giống như làm một cái gì đó như thế này:

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')}

Sản lượng dự kiến:

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

Có cách rõ ràng / pythonic nào để đạt được điều này mà không cần phải lặp lại thủ công qua từng bộ và kiểm tra từng bộ tuple[:3]không?


3
Suy nghĩ ban đầu của tôi là biến chúng thành các lớp, xác định toán tử so sánh
Kenny Ostrom

2
phân lớp setvà ghi đè các hoạt động khác nhau. Không có giải pháp vượt trội nào mà tôi biết và tôi nghi ngờ một giải pháp tồn tại.
Ev. Kounis

Không có "key = ..." hoặc một cái gì đó giống nhau (như đối với sort (..)) cho các bộ. Tuples là bất biến và có thể băm và được so sánh dựa trên hàm băm của chúng. Loại bỏ một yếu tố sẽ làm mất hiệu lực băm. Vì vậy, không - không thể. Nếu bạn không cần giá trị, bạn có thể tạo bộ 3 phần:aa = { t[:3] for t in a }
Patrick Artner

2
@ AK47 Sự khác biệt (bộ) giữa hai bộ S và T được viết S ∖ T và có nghĩa là bộ bao gồm các phần tử của S không phải là các phần tử của T: x∈S Tx∈S∧x∉T
Grajdeanu Alex.

Phân lớp tuplevà ghi đè toán tử khác biệt
Pynchia

Câu trả lời:


10

Đây là cách bạn có thể viết lớp của riêng mình để ghi đè hành vi băm bình thường của một tuple:

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)

với đầu ra

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

Để sửa đổi cách các bộ dữ liệu ứng xử, chúng ta phải sửa đổi cách các bộ dữ liệu được băm.

Từ đây ,

Một đối tượng có thể băm nếu nó có giá trị băm không bao giờ thay đổi trong suốt vòng đời của nó (nó cần một __hash__()phương thức) và có thể so sánh với các đối tượng khác (nó cần một __eq__()phương thức). Các đối tượng có thể băm so sánh bằng nhau phải có cùng giá trị băm.

Hashability làm cho một đối tượng có thể sử dụng như một khóa từ điển và một thành viên được thiết lập, bởi vì các cấu trúc dữ liệu này sử dụng giá trị băm bên trong.

Vì vậy, để làm cho băm bỏ qua phần tử cuối cùng, chúng ta phải quá tải các phương thức dunder __eq____hash__một cách thích hợp. Điều này không phải là quá khó vì tất cả những gì chúng ta phải làm là cắt bỏ phần tử cuối cùng và sau đó ủy thác cho các phương thức thích hợp của một phương thức bình thường tuple.

Đọc thêm:


1
Rât gọn gang! Bạn cũng có thể mô tả một chút làm thế nào điều này hoạt động? Nó có thể có giá trị cho những người sẽ đọc qua giải pháp này.
Grajdeanu Alex.

@GrajdeanuAlex. Tôi đã thêm một lời giải thích ngắn :). Thực sự đó chỉ là kết hợp các bit và các phần của toán tử quá tải và cách băm hoạt động trong Python.
Izaak van Dongen

2

Đây là một cách tiếp cận xác định abvới các danh sách thay vì các tập hợp, vì dường như đối với tôi, giải pháp chuyển tiếp thẳng nhất ngụ ý lập chỉ mục 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
Điều này nếu tôi không nhầm là O (n), vì tôi sử dụng một bộ để tra cứu. Mặc dù tôi nghĩ rằng câu trả lời của Izaak van Dongen thanh lịch hơn nhiều @konrad
yatu

1
Bạn hoàn toàn đúng, việc sử dụng (và liệt kê) một danh sách đã loại bỏ tôi nhưng tất nhiên một sự khác biệt cũng cần phải lặp lại trong tập đầu tiên.
Konrad Rudolph

1

Bộ hoạt động tốt. Đó là dữ liệu của bạn không hoạt động đúng. Nếu chúng trông khác nhau nhưng chúng thực sự giống nhau, thì hãy xác định kiểu dữ liệu hoạt động như bạn muốn. Sau đó thiết lập công trình tuyệt vời của riêng mình.

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
Bạn đã xác định __repr____hash__trong điều khoản của tuples, nhưng không __eq__. Sẽ không phải là ngắn hơn để sử dụng bộ dữ liệu ở đây, quá? Trong thực tế, bạn có thể sử dụng cắt ở đây và trong __hash__để rút ngắn mã hơn nữa.
Konrad Rudolph

Aye, chỉ phân nhóm tuple là một cải tiến lớn cho câu hỏi như đã hỏi.
Kenny Ostrom
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.