I have a weird situation. I have a dict, self.containing_dict. Using the debug probe, I see that dict’s contents and I can see that self is a key of it. But look at this:
>>> self in self.containing_dict
False
>>> self in self.containing_dict.keys()
True
>>> self.containing_dict.has_key(self)
False
What’s going on?
(I will note that this is in a piece of code which gets executed on a weakref callback.)
Update: I was asked to show the __hash__ implementation of self. Here it is:
def __hash__(self):
return hash(
(
tuple(sorted(tuple(self.args))),
self.star_args,
tuple(sorted(tuple(self.star_kwargs)))
)
)
args = property(lambda self: dict(self.args_refs))
star_args = property(
lambda self:
tuple((star_arg_ref() for star_arg_ref in self.star_args_refs))
)
star_kwargs = property(lambda self: dict(self.star_kwargs_refs))
The problem you describe can only be caused by
selfhaving implemented__eq__(or__cmp__) without implementing an accompanying__hash__. If you didn’t implement a__hash__method, you should do so — normally you can’t use objects that define__eq__but not__hash__as dict keys, but if you inherit a__hash__that may slip by.If you do implement
__hash__, you have to make sure it acts the right way: the result must not change over the lifetime of the object (or at least as long as the object is in use as a dict key or set item), and it must be consistent with__eq__. An object’s hash value must be the same as objects it’s equal to (according to its__eq__or__cmp__.) An object’s hash value may be different from objects it’s not equal to, but it doesn’t have to be. The requirements also mean you can not have the result of__eq__change over the lifetime of the object, which is why mutable objects usually can’t be used as dict keys.If your
__hash__and__eq__are not matched up, Python won’t be able to find the object in dicts and sets, but it will still show up indict.keys()andlist(set), which is what you’re describing here. The usual way to implement__hash__methods is by returning thehash()of whatever attributes you use in your__eq__or__cmp__method.