- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有一个类,其实例要通过不同于它们携带的数据值的标识来区分。在我的代码中,我打算使用 ==
来表示两个实例在它们的数据方面是等价的,并且 is
表示两个变量引用同一个实例,也就是说,他们是相同的。根据我的理解,这一切都很正常。
此外,我想在 set
中使用实例(等效或不等效),并作为 dict
中的键。这需要为类定义 __hash__
函数。
但是在这方面我不明白__hash__
的文档要求:
The only required property is that objects which compare equal have the same hash value.
这是否意味着两个不同但等价的对象不能用作 dict
中的不同键,或单独出现在 set
中?在下面的代码中,我通过覆盖 __eq__
和 __hash__
来打破这一要求,以反射(reflect)我的预期用途。它在 Python 2.7 和 3.7 中按预期工作。
如我在此处所做的那样,违反 __hash__
的要求会产生哪些负面后果?
有没有更好的方法来实现我的目标?
class A( object ):
def __init__( self, v1, v2 ):
self.v = ( v1, v2 )
def __eq__( self, b ):
return self.v[0] == b.v[0] and self.v[1] == b.v[1]
def __hash__( self ):
return id( self )
def __str__( self ):
return str( self.v )
p = A( 1, 0 )
q = A( 1, 0 )
print( str( p ), str( q ) )
print( "identical?", p is q )
print( "equivalent?", p == q )
print( "hashes", hash(p), hash(q) )
s = set( [p, q] )
print( "set length", len( s ) )
print( "both in set?", p in s, q in s )
d = { p:3, q:4 }
print( "dict length", len( d ) )
print( "as distinct keys", d[p], d[q] )
最佳答案
The only required property is that objects which compare equal have the same hash value.
规范文本中的“比较相等”表示它们的 __eq__
方法的结果 - 不要求它们是同一对象。
__hash__
认为,必须基于 __eq__
中使用的值,而不是对象的“id”——这部分在您的代码中不正确.为了让它工作,它必须是这样的:
只是做:
...
def __eq__( self, b ):
return self.v[0] == b.v[0] and self.v[1] == b.v[1]
def __hash__( self ):
return hash((self.v[0], self.v[1]))
Does that mean that two distinct but equivalent objects cannot be used as different keys in a dict, or appear individually in a set?
是的。这就是规范的意思。
解决方法是为您的类保留默认的 __eq__
实现以符合规则,并实现您必须在代码中使用的替代形式的比较。
最直接的方法就是保留 __eq__
的默认实现,它按身份进行比较,并有一个任意的方法用于比较,(语言中编码的习语不支持运算符覆盖的必须无论如何使用):
class A( object ):
...
def equals( self, b ):
return self.v[0] == b.v[0] and self.v[1] == b.v[1]
p = A( 1, 0 )
q = A( 1, 0 )
print( str( p ), str( q ) )
print( "identical?", p is q )
print( "equivalent?", p.equals(q) )
有一些方法可以对此进行一些改进 - 但基线是:__eq__
必须符合规则,并进行身份比较。
一种方法是使用一个内部关联对象作为“命名空间”,您可以将其用于比较:
class CompareSpace:
def __init__(self, parent):
self.parent = parent
def __eq__( self, other ):
other = other.parent if isinstance(other, type(self)) else other
return self.parent.v[0] == other.v[0] and other.v[1] == b.parent.v[1]
class A:
def __init__( self, v1, v2 ):
self.v = ( v1, v2 )
self.comp = CompareSpace(self)
def __str__( self ):
return str( self.v )
p = A( 1, 0 )
q = A( 1, 0 )
print( str( p ), str( q ) )
print( "identical?", p is q )
print( "equivalent?", p.comp == q )
print( "hashes", hash(p), hash(q) )
现在我将插入一个例子来说明这是如何中断的——我正在故意创建一个更加中断的类,以确保在第一次尝试时出现问题。但是,即使每 200 万次出现一次问题,您的代码仍然会太破烂,无法用于任何实际应用,即使是个人代码:您将拥有一个不确定的字典:
class Broken:
def __init__(self, name):
self.name = name
def __hash__(self):
return id(self) % 256
def __eq__(self, other):
return True
def __repr__(self):
return self.name
In [23]: objs = [Broken(f"{i:02d}") for i in range(64)]
In [24]: print(objs)
[00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
In [25]: test = {}
In [26]: for obj in objs:
...: if obj not in test:
...: test[obj] = 0
...:
In [27]: print(test)
{00: 0, 01: 0, 02: 0, 11: 0}
# Or with simple, unconditional, insertion:
In [29]: test = {obj: i for i, obj in enumerate(objs)}
In [30]: test
Out[30]: {00: 57, 01: 62, 02: 63, 11: 60}
(我重复一遍,虽然你的 has 值本身不会冲突,但内部字典代码必须将哈希中的数字减少到其哈希表中的索引 - 不一定按模块(%) - 否则每个空字典将需要 2 ** 64 个空条目,并且仅当所有哈希仅为 64 位宽时)
关于Python __hash__ : identity vs. 等价,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58346286/
我是 Mercurial 的新手,并且不知何故仍处于评估过程中,所以这四个概念对我来说有点困惑。有些被提到等同于 Git 的 Staging/Index 概念,有些甚至比 Git 的 Staging
关闭。这个问题需要更多focused .它目前不接受答案。 想改善这个问题吗?更新问题,使其仅关注一个问题 editing this post . 6 个月前关闭。 Improve this ques
任何人都可以给我详细信息吗? 例如? #ID 是属性、特性、选择器还是 anchor ? 默认属性和默认属性是不同的东西吗? 这些都是标签还是元素? 我们将对此说些什么 这个 ..... 还有这些
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 8 年前。 Improve this qu
我有一个由 Javascript 填充的下拉列表。 在决定加载时显示的默认值时,我意识到以下属性显示的值完全相同: innerText innerHTML label text textContent
我可以知道每个 Exec 之间有什么区别吗? , ExecWait , ExecShell , nsExec::Exec , nsExec::ExecToLog, nsExec::ExecToStac
当您处于版本 1 和版本 2 之间时,您会如何维护您的软件? 从我的角度来看,“补丁”、“修补程序”、“维护版本”、“服务包”等术语都很模糊,根据与您交谈的对象不同,定义也不同。 您如何称呼版本之间的
我刚刚发现在 ES6 中有一个新的数学方法:Math.trunc . 我在 MDN article 中阅读了它的描述。 , 听起来像使用 |0 . 此外,>0 , &-1 , ^0也做类似的事情(感谢
我想知道我的 StackPanel 所有项目的高度。 有什么区别: Height - 获取或设置元素的建议高度。 ActualHeight - 获取该元素的渲染高度。 (只读) ExtentHeigh
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 9 年前。 Improve this
我对所有声称以某种方式阻止计算的内置 Mathematica 函数感到困惑:Unevaluated、Defer、Hold ,以及超过 6 个 Hold* 形式。 Mathematica 文档只是单独解
我什至不确定正确的术语,所以让我从我的目标开始:拥有一个简单的应用程序(“Data Doler”),它只会将大量数据从文件读取到内存中,然后提供服务将该数据切片到名为“Data Lapper”的单个多
我刚刚开始在我的项目中使用 Elasticsearch,我想像 sql 关键字一样搜索 '喜欢%' 做。 谁能解释一下 之间的区别通配符 , 前缀 , 查询字符串和 正则表达式 ? 哪个可以搜索最好性
由于我对任何主流浏览器(Firefox、Chrome、Opera)都不太满意,而且我尝试过的不太受欢迎的浏览器(近十几种)都没有,所以我决定 DIY 并制作一个网页我想要最好的浏览器。 主要目标是让它
我知道如何使用 Python 解析页面。我的问题是哪种方法是所有解析技术中最快的,其他方法的速度有多快? 我知道的解析技术有Xpath、DOM、BeautifulSoup,还有使用Python的fin
我试图从正在解析的命令行中找出哪个函数最适合将十进制、十六进制或八进制数转换为 int 最好——在不知道输入的情况下事先。 目标是使用一个函数来识别不同类型的输入并将其分配给它的整数 (int) 值,
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我们需要在我们的网站上显示酒吧、餐馆和剧院等各种场所的元信息(例如,地址、姓名)。 理想情况下,用户会输入地点名称以及邮政编码,我们会提供最接近的匹配项。 人们将哪些 API 用于类似的地理定位目的?
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我正在创建我的第一个 Web 应用程序,我真的很困惑应该使用什么技术。 我的应用程序需要看起来很严肃(像一个应用程序),它不需要很多色彩缤纷的图形界面。它只需要一个工具栏、一个标签栏、一个拆分面板(最
我是一名优秀的程序员,十分优秀!