PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/objspace/std/identitydict.py

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