Author: Armin Rigo <ar...@tunes.org> 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 pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit