Author: Armin Rigo <[email protected]>
Branch:
Changeset: r88147:714537112a18
Date: 2016-11-05 16:04 +0100
http://bitbucket.org/pypy/pypy/changeset/714537112a18/
Log: Issue #2426: reverse the order of 'a.__dict__'. Gives a more
natural-looking order, and should help performance of copy.copy()
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -830,65 +830,83 @@
new_obj = map.materialize_r_dict(space, obj, dict_w)
obj._set_mapdict_storage_and_map(new_obj.storage, new_obj.map)
+
+class IteratorMixin(object):
+
+ def _init(self, strategy, w_dict):
+ w_obj = strategy.unerase(w_dict.dstorage)
+ self.w_obj = w_obj
+ self.orig_map = curr_map = w_obj._get_mapdict_map()
+ # We enumerate non-lazily the attributes, and store them in the
+ # 'attrs' list. We then walk that list in opposite order. This
+ # gives an ordering that is more natural (roughly corresponding
+ # to oldest-first) than the one we get in the direct algorithm
+ # (from leaves to root, looks like backward order). See issue
+ # #2426: it should improve the performance of code like
+ # copy.copy().
+ attrs = []
+ while True:
+ curr_map = curr_map.search(DICT)
+ if curr_map is None:
+ break
+ attrs.append(curr_map.name)
+ curr_map = curr_map.back
+ self.attrs = attrs
+
+
class MapDictIteratorKeys(BaseKeyIterator):
+ objectmodel.import_from_mixin(IteratorMixin)
+
def __init__(self, space, strategy, w_dict):
BaseKeyIterator.__init__(self, space, strategy, w_dict)
- w_obj = strategy.unerase(w_dict.dstorage)
- self.w_obj = w_obj
- self.orig_map = self.curr_map = w_obj._get_mapdict_map()
+ self._init(strategy, w_dict)
def next_key_entry(self):
assert isinstance(self.w_dict.get_strategy(), MapDictStrategy)
if self.orig_map is not self.w_obj._get_mapdict_map():
return None
- if self.curr_map:
- curr_map = self.curr_map.search(DICT)
- if curr_map:
- self.curr_map = curr_map.back
- attr = curr_map.name
- w_attr = self.space.wrap(attr)
- return w_attr
+ attrs = self.attrs
+ if len(attrs) > 0:
+ attr = attrs.pop()
+ w_attr = self.space.wrap(attr)
+ return w_attr
return None
class MapDictIteratorValues(BaseValueIterator):
+ objectmodel.import_from_mixin(IteratorMixin)
+
def __init__(self, space, strategy, w_dict):
BaseValueIterator.__init__(self, space, strategy, w_dict)
- w_obj = strategy.unerase(w_dict.dstorage)
- self.w_obj = w_obj
- self.orig_map = self.curr_map = w_obj._get_mapdict_map()
+ self._init(strategy, w_dict)
def next_value_entry(self):
assert isinstance(self.w_dict.get_strategy(), MapDictStrategy)
if self.orig_map is not self.w_obj._get_mapdict_map():
return None
- if self.curr_map:
- curr_map = self.curr_map.search(DICT)
- if curr_map:
- self.curr_map = curr_map.back
- attr = curr_map.name
- return self.w_obj.getdictvalue(self.space, attr)
+ attrs = self.attrs
+ if len(attrs) > 0:
+ attr = attrs.pop()
+ return self.w_obj.getdictvalue(self.space, attr)
return None
class MapDictIteratorItems(BaseItemIterator):
+ objectmodel.import_from_mixin(IteratorMixin)
+
def __init__(self, space, strategy, w_dict):
BaseItemIterator.__init__(self, space, strategy, w_dict)
- w_obj = strategy.unerase(w_dict.dstorage)
- self.w_obj = w_obj
- self.orig_map = self.curr_map = w_obj._get_mapdict_map()
+ self._init(strategy, w_dict)
def next_item_entry(self):
assert isinstance(self.w_dict.get_strategy(), MapDictStrategy)
if self.orig_map is not self.w_obj._get_mapdict_map():
return None, None
- if self.curr_map:
- curr_map = self.curr_map.search(DICT)
- if curr_map:
- self.curr_map = curr_map.back
- attr = curr_map.name
- w_attr = self.space.wrap(attr)
- return w_attr, self.w_obj.getdictvalue(self.space, attr)
+ attrs = self.attrs
+ if len(attrs) > 0:
+ attr = attrs.pop()
+ w_attr = self.space.wrap(attr)
+ return w_attr, self.w_obj.getdictvalue(self.space, attr)
return None, None
diff --git a/pypy/objspace/std/test/test_mapdict.py
b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -1183,6 +1183,20 @@
got = a.method()
assert got == 43
+ def test_dict_order(self):
+ # the __dict__ order is not strictly enforced, but in
+ # simple cases like that, we want to follow the order of
+ # creation of the attributes
+ class A(object):
+ pass
+ a = A()
+ a.x = 5
+ a.z = 6
+ a.y = 7
+ assert list(a.__dict__) == ['x', 'z', 'y']
+ assert a.__dict__.values() == [5, 6, 7]
+ assert list(a.__dict__.iteritems()) == [('x', 5), ('z', 6), ('y', 7)]
+
def test_bug_method_change(self):
class A(object):
def method(self):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit