Set farkı yaparken son elemanı görmezden gelmenin pitonik yolu


11

Diyelim ki iki tane var 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')}

Şimdi, yapmak istediğim şey set farkını bulmak b \ aama her bir tuptan son elementi görmezden gelmek. Yani böyle bir şey yapmak gibi:

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

Beklenen çıktı:

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

Her set üzerinde manuel olarak yineleme yapmak ve her birine karşı kontrol etmek zorunda kalmadan bunu başarmanın açık / pitonik bir yolu var mı tuple[:3]?


3
İlk düşüncem onları sınıf yapmak, karşılaştırma operatörünü tanımlamak
Kenny Ostrom

2
alt setve fark işlemi üzerine yazın. Bildiğim hazır bir çözüm yok ve birinin var olduğundan şüpheliyim.
Ev. Kounis

Setler için "key = ..." veya benzeri bir şey yoktur (sıralama (..) için olduğu gibi). Tupller yapışmaz ve yıkanabilir ve hashlerine göre karşılaştırılır. Bir öğenin kaldırılması karmayı geçersiz kılar. Yani Hayır - mümkün değil. Değere ihtiyacınız yoksa 3 parçalı setler oluşturabilirsiniz:aa = { t[:3] for t in a }
Patrick Artner

2
@ AK47 İki set S ve T arasındaki (set) farkı S ∖ T olarak yazılır ve S'nin T unsurları olmayan unsurlarından oluşan set anlamına gelir: x∈S ∖ T⟺x∈S∧x∉T
Grajdeanu Alex.

tupleFark operatörünü alt sınıflara
ayırın

Yanıtlar:


10

Bir grubun normal karma davranışını geçersiz kılmak için kendi sınıfınızı nasıl yazabileceğiniz aşağıda açıklanmıştır:

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)

çıktı ile

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

Tuples setlerinin davranış biçimini değiştirmek için tupleslerin karmaşasını değiştirmeliyiz.

Gönderen burada ,

Bir nesne, kullanım ömrü boyunca hiçbir zaman değişmeyen bir karma değere sahipse (bir __hash__()yönteme ihtiyaç duyar ) ve diğer nesnelerle karşılaştırılabildiğinde (bir __eq__()yönteme ihtiyaç duyarsa) yıkanabilir . Eşit karşılaştırılabilen yıkanabilir nesneler aynı karma değerine sahip olmalıdır.

Hashability, bir nesneyi sözlük anahtarı ve küme üyesi olarak kullanılabilir hale getirir, çünkü bu veri yapıları karma değerini dahili olarak kullanır.

Bu nedenle, karma işleminin son öğeyi yoksaymasını sağlamak için, dunder yöntemlerini aşırı yüklemeliyiz __eq__ve __hash__uygun şekilde. Bu o kadar zor olmuyor çünkü tek yapmamız gereken son elementi dilimlemek ve sonra normalin uygun yöntemlerine delege etmektir tuple.

Daha fazla okuma:


1
Çok temiz! Bunun nasıl çalıştığını biraz açıklayabilir misiniz? Bu çözümü okuyacak olanlar için değerli olabilir.
Grajdeanu Alex.

@GrajdeanuAlex. Kısa bir açıklama ekledim :). Gerçekten sadece bitleri ve operatör aşırı yüklenmesini ve Python'da karma işlemenin nasıl çalıştığını birleştiriyor.
Izaak van Dongen

2

Bana göre, en basit çözümün dizin oluşturmayı gerektirdiği anlaşılıyor, çünkü kümeler yerine listeleri tanımlayan ave blisteler içeren bir yaklaşım 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
Arama için bir set kullandığım için bu yanlış yapmıyorsam O (n). Izaak van Dongen'in cevabının çok daha zarif olduğunu düşünmeme rağmen @konrad
yatu

1
Tamamen haklısın, bir listenin kullanılması (ve numaralandırılması) beni attı, ancak elbette belirli bir farkın da ilk set üzerinde tekrar etmesi gerekiyor.
Konrad Rudolph

1

Setler iyi çalışıyor. Verileriniz doğru çalışmıyor. Farklı görünüyorlar, ancak aslında aynılarsa, istediğiniz gibi davranan bir veri türü tanımlayın. Sonra set kendi başına harika çalışıyor.

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
Sen tanımlanmış __repr__ve __hash__küpe açısından değil __eq__. Burada da tuple kullanmak daha kısa olmaz mı? Aslında, __hash__kodu daha da kısaltmak için burada ve içinde dilimlemeyi kullanabilirsiniz .
Konrad Rudolph

Evet, sadece alt sınıf tuple sorulan soru için büyük bir gelişmeydi.
Kenny Ostrom
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.