วิธี Pythonic ของการละเว้นองค์ประกอบสุดท้ายเมื่อทำแตกต่าง


11

สมมติว่าฉันมีสอง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 \ aแต่ไม่สนใจองค์ประกอบสุดท้ายจากทุก tuple ดังนั้นมันเหมือนกับทำอะไรเช่นนี้:

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 = ... " หรืออะไรที่เหมือนกัน (สำหรับ sort (.. )) สำหรับชุด สิ่งอันดับเป็นสิ่งที่ไม่สามารถเปลี่ยนแปลงได้และสับได้และจะถูกเปรียบเทียบบนพื้นฐานของแฮช การลบองค์ประกอบหนึ่งจะทำให้แฮชว่าง ดังนั้นไม่ - เป็นไปไม่ได้ หากคุณไม่ต้องการค่าคุณสามารถสร้างชุด 3 ส่วน:aa = { t[:3] for t in a }
Patrick Artner

2
@ AK47 ความแตกต่าง (ชุด) ระหว่างสองชุด S และ T เขียน S ∖ T และหมายถึงชุดที่ประกอบด้วยองค์ประกอบของ S ซึ่งไม่ใช่องค์ประกอบของ T: x∈S∖T⟺x∈S∧x∉T
Grajdeanu Alex

คลาสย่อยtupleและแทนที่ตัวดำเนินการที่แตกต่าง
Pynchia

คำตอบ:


10

นี่คือวิธีที่คุณจะเขียนชั้นเรียนของคุณเองเพื่อแทนที่พฤติกรรมการแฮ็กตามปกติของ 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)

กับเอาท์พุท

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

ในการปรับเปลี่ยนวิธีการทำงานของชุดสิ่งอันดับเราต้องปรับเปลี่ยนวิธีการแฮชของสิ่งอันดับ

จากที่นี่ ,

วัตถุนั้น hashable ถ้ามันมีค่าแฮซึ่งไม่เคยเปลี่ยนแปลงในช่วงชีวิตของมัน (มันต้องการ__hash__()วิธีการ) และสามารถเปรียบเทียบกับวัตถุอื่น ๆ (มันต้องการ__eq__()วิธีการ) วัตถุที่แฮชที่เปรียบเทียบได้จะต้องมีค่าแฮชเหมือนกัน

Hashability ทำให้วัตถุที่ใช้งานได้เป็นคีย์พจนานุกรมและสมาชิกชุดเนื่องจากโครงสร้างข้อมูลเหล่านี้ใช้ค่าแฮชภายใน

ดังนั้นเพื่อให้การแปลงแป้นพิมพ์เพิกเฉยองค์ประกอบสุดท้ายเราจึงต้องใช้วิธีการ dunder มากเกินไป__eq__และ__hash__เหมาะสม tupleนี้ไม่ได้จบลงด้วยการที่ยากลำบากเช่นนี้เพราะทุกสิ่งที่เราต้องทำคือการปาดองค์ประกอบสุดท้ายแล้วมอบหมายให้วิธีการที่เหมาะสมในการปกติ

อ่านเพิ่มเติม:


1
เรียบร้อยมาก! คุณช่วยอธิบายถึงวิธีการทำงานของมันได้บ้าง มันอาจจะคุ้มค่าสำหรับคนที่จะอ่านวิธีนี้
Grajdeanu Alex

@GrajdeanuAlex :)ฉันได้เพิ่มคำอธิบายสั้น ๆ จริงๆแล้วมันเป็นเพียงการรวมบิตและชิ้นส่วนของการใช้งานเกินพิกัดและวิธีการแปลงแป้นพิมพ์ทำงานใน Python
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 van 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__ในแง่ของ tuples __eq__แต่ไม่ มันจะสั้นกว่าไหมถ้าใช้สิ่งอันดับนี่ด้วย ในความเป็นจริงคุณสามารถใช้การแบ่งที่นี่และใน__hash__การย่อรหัสเพิ่มเติม
Konrad Rudolph

ใช่เพียงแค่ซับคลาสคลาสย่อยเป็นการปรับปรุงครั้งใหญ่สำหรับคำถามที่ถาม
Kenny Ostrom
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.