Python:如何使 set/dict 将相同类型的可比较可哈希对象视为单个对象?
Python: how to make comparable hashable objects of the same type to be treated by set/dict as the single object?
我希望set
/dict
把同类型的对象当成单个对象,这样[=14中的同类型对象不会超过一个=]/dict
.
我还希望这些对象能够以非平凡的方式进行比较,例如针对它们的内部状态:C(value=1) == C(value=2)
必须 return False
和 C(value=1) == C(value=1)
必须 return True
.
天真的方法失败了:
class Base(object):
KEY = 'base'
def __init__(self, value):
self.value = value
def __hash__(self):
# hash is the same for all objects of the same type
return hash(type(self).KEY)
def __eq__(self, other):
# objects are compared against `.value` attribute
return bool(self.value == other.value)
def __repr__(self):
return '{}({})'.format(type(self).__name__, self.value)
class Derived(Base):
KEY = 'derived'
print {Base(1), Base(2), Derived(3), Derived(4)}
# actual: set([Base(1), Base(2), Derived(3), Derived(4)])
# desired: set([Base(?), Derived(?)]) -- "?" for arbitrary value
print {Base(1): 1, Base(2): 2}
# actual: {Base(1): 1, Base(2): 2}
# desired: {Base(?): ?} -- "?" for arbitrary value
是否可以将用户定义的 class 对象存储在 set/dict 中,使得 set/dict 中不超过一个具有相同对象的对象class,但让这些对象仍然具有非平凡的可比性?
我知道我可以使用类型作为键将对象存储在 dict
中:
d = {
Base: Base(1),
Derived: Derived(2),
}
但是这种方式不能解决set
/frozenset
的问题。
比较相等的对象应该具有相同的散列值——这并不排除在设计上比较不同的对象也具有相同的散列值。
但是,如果对象的哈希值相同,Python 然后求助于 __eq__
来区分对象 - 这是哈希概念的一部分,因为哈希冲突可能发生在任何地方。如果出现相等的哈希值和不同的相等性,则对象被认为对于字典和集合效果不同。
为了实现只允许字典或集合中的每种对象类型之一的目标,它们也必须全部比较相等。
因此,给你的简短回答是:你想要的是不可能的。
我建议您的一个解决方法是不要使用 __eq__
来比较这些对象,而是比较然后在需要时使用其他方法,(就像 Java 人们必须做的那样.equals
几乎所有的方法)。
您可以有一个辅助包装器 class 以便在您想要比较它们时使用:
class E(object):
def __init__(self, obj):
self.obj = obj
def __eq__(self, other):
return self.obj.value == getattr(other, "obj", other).value
而他们,每当你需要进行比较时,只要做:if E(Base(1)) == Base(2): ...
我希望set
/dict
把同类型的对象当成单个对象,这样[=14中的同类型对象不会超过一个=]/dict
.
我还希望这些对象能够以非平凡的方式进行比较,例如针对它们的内部状态:C(value=1) == C(value=2)
必须 return False
和 C(value=1) == C(value=1)
必须 return True
.
天真的方法失败了:
class Base(object):
KEY = 'base'
def __init__(self, value):
self.value = value
def __hash__(self):
# hash is the same for all objects of the same type
return hash(type(self).KEY)
def __eq__(self, other):
# objects are compared against `.value` attribute
return bool(self.value == other.value)
def __repr__(self):
return '{}({})'.format(type(self).__name__, self.value)
class Derived(Base):
KEY = 'derived'
print {Base(1), Base(2), Derived(3), Derived(4)}
# actual: set([Base(1), Base(2), Derived(3), Derived(4)])
# desired: set([Base(?), Derived(?)]) -- "?" for arbitrary value
print {Base(1): 1, Base(2): 2}
# actual: {Base(1): 1, Base(2): 2}
# desired: {Base(?): ?} -- "?" for arbitrary value
是否可以将用户定义的 class 对象存储在 set/dict 中,使得 set/dict 中不超过一个具有相同对象的对象class,但让这些对象仍然具有非平凡的可比性?
我知道我可以使用类型作为键将对象存储在 dict
中:
d = {
Base: Base(1),
Derived: Derived(2),
}
但是这种方式不能解决set
/frozenset
的问题。
比较相等的对象应该具有相同的散列值——这并不排除在设计上比较不同的对象也具有相同的散列值。
但是,如果对象的哈希值相同,Python 然后求助于 __eq__
来区分对象 - 这是哈希概念的一部分,因为哈希冲突可能发生在任何地方。如果出现相等的哈希值和不同的相等性,则对象被认为对于字典和集合效果不同。
为了实现只允许字典或集合中的每种对象类型之一的目标,它们也必须全部比较相等。
因此,给你的简短回答是:你想要的是不可能的。
我建议您的一个解决方法是不要使用 __eq__
来比较这些对象,而是比较然后在需要时使用其他方法,(就像 Java 人们必须做的那样.equals
几乎所有的方法)。
您可以有一个辅助包装器 class 以便在您想要比较它们时使用:
class E(object):
def __init__(self, obj):
self.obj = obj
def __eq__(self, other):
return self.obj.value == getattr(other, "obj", other).value
而他们,每当你需要进行比较时,只要做:if E(Base(1)) == Base(2): ...