1 2 3 4 5 6 7 8 9 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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
## ----------------------------------------------------------------------------
## dict strategy (see dictmultiobject.py)
from rpython.rlib import rerased
from rpython.rlib.debug import mark_dict_non_null
from pypy.objspace.std.dictmultiobject import (AbstractTypedStrategy,
DictStrategy,
create_iterator_classes)
# this strategy is selected by EmptyDictStrategy.switch_to_correct_strategy
class IdentityDictStrategy(AbstractTypedStrategy, DictStrategy):
"""
Strategy for custom instances which compares by identity (i.e., the
default unless you override __hash__, __eq__ or __cmp__). The storage is
just a normal RPython dict, which has already the correct by-identity
semantics.
Note that at a first sight, you might have problems if you mutate the
class of an object which is already inside an identitydict. Consider this
example::
class X(object):
pass
d = {x(): 1}
X.__eq__ = ...
d[y] # might trigger a call to __eq__?
We want to be sure that x.__eq__ is called in the same cases as in
CPython. However, as long as the strategy is IdentityDictStrategy, the
__eq__ will never be called.
It turns out that it's not a problem. In CPython (and in PyPy without
this strategy), the __eq__ is called if ``hash(y) == hash(x)`` and ``x is
not y``. Note that hash(x) is computed at the time when we insert x in
the dict, not at the time we lookup y.
Now, how can hash(y) == hash(x)? There are two possibilities:
1. we write a custom __hash__ for the class of y, thus making it a not
"compares by reference" type
2. the class of y is "compares by reference" type, and by chance the
hash is the same as x
In the first case, the getitem immediately notice that y is not of the
right type, and switches the strategy to ObjectDictStrategy, then the
lookup works as usual.
The second case is completely non-deterministic, even in CPython.
Depending on the phase of the moon, you might call the __eq__ or not, so
it is perfectly fine to *never* call it. Morever, in practice with the
minimark GC we never have two live objects with the same hash, so it would
never happen anyway.
"""
erase, unerase = rerased.new_erasing_pair("identitydict")
erase = staticmethod(erase)
unerase = staticmethod(unerase)
def wrap(self, unwrapped):
return unwrapped
def unwrap(self, wrapped):
return wrapped
def get_empty_storage(self):
d = {}
mark_dict_non_null(d)
return self.erase(d)
def is_correct_type(self, w_obj):
w_type = self.space.type(w_obj)
return w_type.compares_by_identity()
def _never_equal_to(self, w_lookup_type):
return False
def w_keys(self, w_dict):
return self.space.newlist(self.unerase(w_dict.dstorage).keys())
create_iterator_classes(IdentityDictStrategy)
|