PageRenderTime 163ms CodeModel.GetById 121ms app.highlight 4ms RepoModel.GetById 36ms app.codeStats 0ms

/pypy/objspace/std/identitydict.py

https://bitbucket.org/pypy/pypy/
Python | 82 lines | 49 code | 8 blank | 25 comment | 13 complexity | f66b1b403c99d8887c9b046a7af2b264 MD5 | raw file
 1## ----------------------------------------------------------------------------
 2## dict strategy (see dictmultiobject.py)
 3
 4from rpython.rlib import rerased
 5from rpython.rlib.debug import mark_dict_non_null
 6from pypy.objspace.std.dictmultiobject import (AbstractTypedStrategy,
 7                                               DictStrategy,
 8                                               create_iterator_classes)
 9
10
11# this strategy is selected by EmptyDictStrategy.switch_to_correct_strategy
12class IdentityDictStrategy(AbstractTypedStrategy, DictStrategy):
13    """
14    Strategy for custom instances which compares by identity (i.e., the
15    default unless you override __hash__, __eq__ or __cmp__).  The storage is
16    just a normal RPython dict, which has already the correct by-identity
17    semantics.
18
19    Note that at a first sight, you might have problems if you mutate the
20    class of an object which is already inside an identitydict.  Consider this
21    example::
22
23    class X(object):
24        pass
25    d = {x(): 1}
26    X.__eq__ = ...
27    d[y] # might trigger a call to __eq__?
28
29    We want to be sure that x.__eq__ is called in the same cases as in
30    CPython.  However, as long as the strategy is IdentityDictStrategy, the
31    __eq__ will never be called.
32
33    It turns out that it's not a problem.  In CPython (and in PyPy without
34    this strategy), the __eq__ is called if ``hash(y) == hash(x)`` and ``x is
35    not y``.  Note that hash(x) is computed at the time when we insert x in
36    the dict, not at the time we lookup y.
37
38    Now, how can hash(y) == hash(x)?  There are two possibilities:
39
40      1. we write a custom __hash__ for the class of y, thus making it a not
41        "compares by reference" type
42
43      2. the class of y is "compares by reference" type, and by chance the
44         hash is the same as x
45
46    In the first case, the getitem immediately notice that y is not of the
47    right type, and switches the strategy to ObjectDictStrategy, then the
48    lookup works as usual.
49
50    The second case is completely non-deterministic, even in CPython.
51    Depending on the phase of the moon, you might call the __eq__ or not, so
52    it is perfectly fine to *never* call it.  Morever, in practice with the
53    minimark GC we never have two live objects with the same hash, so it would
54    never happen anyway.
55    """
56
57    erase, unerase = rerased.new_erasing_pair("identitydict")
58    erase = staticmethod(erase)
59    unerase = staticmethod(unerase)
60
61    def wrap(self, unwrapped):
62        return unwrapped
63
64    def unwrap(self, wrapped):
65        return wrapped
66
67    def get_empty_storage(self):
68        d = {}
69        mark_dict_non_null(d)
70        return self.erase(d)
71
72    def is_correct_type(self, w_obj):
73        w_type = self.space.type(w_obj)
74        return w_type.compares_by_identity()
75
76    def _never_equal_to(self, w_lookup_type):
77        return False
78
79    def w_keys(self, w_dict):
80        return self.space.newlist(self.unerase(w_dict.dstorage).keys())
81
82create_iterator_classes(IdentityDictStrategy)