为什么以及如何使用Python函数?

时间:2016-07-22 05:30:35

标签: python hash

我最近在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根据类型计算的一些自动化方法。

使功能可以清洗的动机是什么?对于奖金,函数的哈希是什么?

3 个答案:

答案 0 :(得分:43)

没什么特别的。如您所见,检查函数类型的未绑定__hash__方法:

>>> def f(): pass
...
>>> type(f).__hash__
<slot wrapper '__hash__' of 'object' objects>

of 'object' objects部分表示它只是从__hash__继承了默认的基于身份的object。功能==hash按身份运作。对于继承id的任何类型,hashobject.__hash__之间的差异是正常的:

>>> x = object()
>>> id(x)
40145072L
>>> hash(x)
2509067

您可能认为__hash__仅应该为不可变对象定义,并且您几乎是正确的,但是缺少关键细节。 __hash__只应为 ==比较中涉及的所有内容不可变的对象定义。对于==基于身份的对象,基于hash的身份也是完全标准的,因为即使对象是可变的,它们也不可能以改变它们的方式变得可变身份。具有基于身份的==的文件,模块和其他可变对象都以这种方式运行。

答案 1 :(得分:22)

例如,创建函数对象集或通过函数索引字典可能是有用的。不可变对象通常支持__hash__。在任何情况下,由deflambda定义的函数之间没有内部差异 - 这纯粹是语法。

使用的算法取决于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

答案 2 :(得分:3)

一个函数是可清除的,因为它是一个普通的,内置的,不可变的对象。

来自Python Manual

  

如果对象具有在其生命周期内永远不会更改的哈希值(它需要__hash__()方法),并且可以与其他对象进行比较(它需要__eq__()或{{ 1}}方法)。比较相等的Hashable对象必须具有相同的哈希值。

     

Hashability使对象可用作字典键和set成员,因为这些数据结构在内部使用哈希值。

     

所有Python的不可变内置对象都是可清除的,而没有可变容器(例如列表或字典)。默认情况下,作为用户定义类实例的对象是可清除的;它们都比较不相等(除了它们自己),它们的哈希值来自它们的__cmp__()