简体   繁体   中英

Why is this python class instance hashable?

This is pertaining to python 2.x

In the following class, if we subclass " object ", I understand the methods are inherited in the derived class Foo which includes __hash__ (can see this by printing dir(Foo() )

Hence calling hash(Foo()) calls the magic method __hash__ and gives us a hash value.

However, if we don't subclass " object ", resulting in dir(Foo()) not listing out the __hash__ method, so then why do we still get a hash value in python2?

I believe in python3 this problem has been addressed since the methods from the "object*" class are inherited by default.

#class Foo(object) Works since __hash__ is available in the base class 
class Foo:  #Why does this work? 
    def __init__(self):
        self.x = None
a = Foo()
print dir(a) # No __hash__ magic method
print hash(a) 
# Expecting an error like non-hashable or __hash__ not implemented 
# or something similar

Old-style classes are weird. Officially, instances of old-style classes aren't wholly instances of their class, they're all instances of type instance . The instance type defines __hash__ ( tp_hash is the C level slot that's equivalent to __hash__ for C defined types), so even though it's not defined on your instance directly, nor on the class that created it, it finds __hash__ on the instance type itself through weird and terrible magic (actually, the magic is in how it manages to use your class's features at all, given that its type is instance ).

You can see this in the interactive interpreter:

>>> class Foo: pass

>>> Foo().__hash__  # Same basic error for Foo.__hash__ too
AttributeError                            Traceback (most recent call last)
...
----> 1 Foo().__hash__

AttributeError: Foo instance has no attribute '__hash__'
>>> type(Foo())
<type 'instance'>
>>> type(Foo()).__hash__
<slot wrapper '__hash__' of 'instance' objects>

This works even though the instance itself can't see __hash__ because "special methods" (those documented special methods that begin and end with double underscores) are looked up on the type, not the instance, so __hash__ is found on instance itself. At the C level, hash(x) is doing the equivalent of type(x).__hash__(x) (it's a little more complicated because it won't use the default __hash__ implementation if __eq__ has a custom definition, but that's the general idea).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM