Blog Cover Image

Inspire you to have New thinking, Walk out your unique Road.

有的時候,你無意間遇到的一些故事,會激發你的靈感,改變你的想法,接下來你會用與之前全然不同的觀念去創造屬於你獨特的故事。

Sign @MinaYu.

[技術小筆記] 利用eq, hash 解決去除重複物件(object)

Posted on

恩… 我重新改寫一下這篇

因為在我發文的後幾天我解決了,且在前輩的幫助下我有了更新一層的領悟

我有點不知道這篇的知識該擺在哪,小筆記當初規劃是開來存放每次解決的小問題

今天介紹 __eq__, __hash__在Python 物件Object的做法


起初是因為一連串重複的object list中取除獨特的物件list

若set() 使用在數值上,是可以直接去除重複值的

>>> list = [2,3,4,5,5,5,5,2,2,3,4,5]
>>> list
[2, 3, 4, 5, 5, 5, 5, 2, 2, 3, 4, 5]
>>> set(list)
{2, 3, 4, 5}

所以代表set利用在數字上是可以去除重複值

假設我的物件List長這樣

o_list = [data, data, data, data, data, data]

其中只會有3個物件是獨立的,但利用set()去實作並沒有篩選出獨立的3個物件

我先後參考這些文件撰寫出程式碼

How does a Python set([]) check if two objects are equal? What methods does an object need to define to customise this?

How to remove duplicates in set for objects?

以及許多

然後造樣寫了一個這個

class RemoveDuplicate(object):

    def __init__(self, data):
        self.data = data.data
        self.id = data.id
        self.column = data.column_name
        self.table = data.table_name

    def __eq__(self, other):
        return (self.id) == (other.id)

    def __hash__(self):
        return hash(self.id)

data_set = set()
for data, book in library:
    for each_data in list(data):
        data_list.add(RemoveDuplicate(each_data))

最後比較折騰我的原因是因為我的程式實際上傳的data 資料是 Object 裡又包Object

而且剛好內部的object 是unhashable

我的data簡單來說長這樣

data
    id = 1
    object(OrderedDict)
    column = "..."
    table = "..."

在我的data中有包了一個object 是 OrderedDict,他是不可被hash


來介紹一下此篇 Object, Equal, Hash

Equal 這個功能,前輩說在各大程式語言內都看得到,例如JAVA  openhome.ccCodeData

例如C #

前輩丟了這篇給我讓我捉模一下 Python Hashes and Equality

Equal 在這寫是檢查兩個物件有沒有相同

而相同的依據就是 __eq__ 去定義

def __eq__(self, other):
    return (self.id) == (other.id)

在我的程式碼內是使用data.id 若相同就是同個物件

什麼Hash? 雜湊函式

講一個例子就是有一副樸克牌,Hash的元件為紅心,黑桃,方塊及梅花

接下來塞52張不同花色的牌卡下去後,經過Hash,樸克牌會像牌七一樣,同樣花色的排一排,該是紅心的牌他就會排在紅心的List後

紅心, [紅心Q, 紅心3, 紅心1, ...]

黑桃, [黑桃1, 黑桃K, 黑桃2,...]

Hash 算是將物件中的某幾個元件(自己定)建立為固定的指紋, 若資料符合一系列的指紋就會被識別出來

而且可被Hash(Hashable)的元件要是不可變動(Immutable),這樣才能拿來作eq比較

# 我用id 當hash
def __hash__(self):
    return hash(self.id)

所以__eq__ + hash ,就是hash會篩選出不同的桶子,而eq則會去比對說不同桶子內若其中有什麼元件是相同的,就會判定兩個桶子(物件)是一樣的

根據Python2.7文章中,

要使用Set做eq比較,唯有被hash過的物件才會被放進eq比較

若Class Object沒有__eq__() 的話,那就不用__hash__()

可是如果有使用__eq__() ,就一定要用__hash__()

意思是寫了eq, 就要用hash,而set會自動呼叫__hash__()

來看一下Python 3與 2的文件 set 3.6 及 set 2.7

set 內的元件必須要可被Hash
The elements of a set must be <span class="xref std std-term">hashable</span>.




set不能包含可變動的元件,像是list或字典。
但他可包含不可變動的群組,像是tuple
As a result, <strong>sets cannot contain mutable elements</strong> such as lists or dictionaries.
However, they <strong>can contain immutable collections</strong> such as tuples or instances of<code class="xref py py-class docutils literal notranslate"><span class="pre">ImmutableSet</span></code>.

甚麼是Mutable/ Immutable?

我找了一篇文章

Mutable vs Immutable Objects in Python

裡面提及

**Mutable objects(我就翻為可變動物件)**:
list, dict, set, byte array
**Immutable objects(不可變動):**
int, float, complex, string, tuple, frozen set [note: immutable version of set], bytes

要使用Set 及 __eq__和 hash

首先確定eq的條件,接著

只有被hash的元件才會拿來做比較

set 會自動執行hash, 若沒有hash函式就不會進到eq

只有可被hash(不可變動的元件Immutable),才可做為eq 做比對

至於如果遇到字典, 串列,或者像我的例子中OrderedDict’,真的要對這些變動元件做hash怎麼辦呢?

在我的data 物件中,包含了OrderedDict, 而OrderedDict,屬於Dictionary的一種

Dictionary 是 Mutable objects, 不可被hash

Python unhashable type: ‘OrderedDict’

In your case, var1 contains some object that is not hashable (it does not implement hash()). This object is an OrderedDict, which is a mutable object and is not hashable by design.

對於unhashable的物件,你可使用

frozenset() 回傳

以下是我的程式碼

class RemoveDuplicate(object):
    def __init__(self, data):
        <strong>self.data = data.data</strong>
        self.id = data.id
        self.column = data.column_name
        self.table = data.table_name

    def __eq__(self, other):
        return (self.id) == (other.id)

    def __hash__(self):
        return hash((self.id, <strong>frozenset(self.data)</strong>, self.table, self.column))

data_set = set()
for data, book in library:
    for each_data in list(data):
        data_list.add(RemoveDuplicate(each_data))

粗字是OrderedDict 物件,可以使用這樣的方法來Hash

參考文章, 此篇可以解決dict unhashable