[英]Why and how are Python functions hashable?
我最近在 Python 中嘗試了以下命令:
>>> {lambda x: 1: 'a'}
{<function __main__.<lambda>>: 'a'}
>>> def p(x): return 1
>>> {p: 'a'}
{<function __main__.p>: 'a'}
兩個dict
創建的成功表明 lambda 和常規函數都是可散列的。 (類似於{[]: 'a'}
失敗, TypeError: unhashable type: 'list'
)。
哈希顯然不一定是函數的 ID:
>>> m = lambda x: 1
>>> id(m)
140643045241584
>>> hash(m)
8790190327599
>>> m.__hash__()
8790190327599
最后一個命令顯示__hash__
方法是為lambda
顯式定義的,也就是說,這不是 Python 基於類型計算的一些自動的東西。
使函數可哈希化的動機是什么? 作為獎勵,函數的哈希值是多少?
這沒什么特別的。 如您檢查函數類型的未綁定__hash__
方法__hash__
:
>>> def f(): pass
...
>>> type(f).__hash__
<slot wrapper '__hash__' of 'object' objects>
of 'object' objects
部分意味着它只是從object
繼承默認的基於身份的__hash__
。 函數==
和hash
按身份工作。 id
和hash
之間的區別對於任何繼承object.__hash__
類型都是正常的:
>>> x = object()
>>> id(x)
40145072L
>>> hash(x)
2509067
您可能認為__hash__
只應該為不可變對象定義,而且您幾乎是對的,但這缺少一個關鍵細節。 __hash__
應該只為==
比較中涉及的所有內容都是不可變的對象定義。 對於==
基於身份的對象,基於身份的hash
也是完全標准的,因為即使對象是可變的,它們也不可能以改變其身份的方式可變。 具有基於身份的==
文件、模塊和其他可變對象都以這種方式運行。
它可能很有用,例如,創建函數對象集,或按函數索引字典。 不可變對象通常支持__hash__
。 在任何情況下,由def
或由lambda
定義的函數之間沒有內部差異 - 這純粹是語法上的。
使用的算法取決於 Python 的版本。 看起來您正在 64 位機器上使用最新版本的 Python。 在這種情況下,函數對象的散列是其id()
右旋轉 4 位,結果被視為一個有符號的 64 位整數。 完成右移是因為對象地址( id()
結果)通常是對齊的,因此它們的最后 3 或 4 位始終為 0,這對於散列函數來說是一個有點煩人的屬性。
在您的具體示例中,
>>> i = 140643045241584 # your id() result
>>> (i >> 4) | ((i << 60) & 0xffffffffffffffff) # rotate right 4 bits
8790190327599 # == your hash() result
一個函數是可散列的,因為它是一個普通的、內置的、非可變的對象。
如果一個對象的哈希值在其生命周期內永遠不會改變(它需要一個
__hash__()
方法),並且可以與其他對象進行比較(它需要一個__eq__()
或__cmp__()
方法),那么它就是可哈希的。 比較相等的可散列對象必須具有相同的散列值。哈希能力使對象可用作字典鍵和集合成員,因為這些數據結構在內部使用哈希值。
Python 的所有不可變內置對象都是可散列的,而沒有可變容器(例如列表或字典)是可散列的。 默認情況下,作為用戶定義類實例的對象是可散列的; 它們都比較不相等(除了它們自己),它們的哈希值來自它們的
id()
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.