This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author jab
Recipients jab
Date 2021-02-17.22:12:32
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1613599952.95.0.0689652491879.issue43246@roundup.psfhosted.org>
In-reply-to
Content
If I understand correctly, it should be an invariant that in the code below, for all "Parent" classes, for all "method"s, Child1.method should return the same result as Child2.method:

```
class Parent:
    def __init__(self, value):
        self._value = value

    def method(self):
        return self._value


class Child1(Parent):
    pass


c1 = Child1(42)
result = c1.method()
assert result == 42, result


class Child2(Parent):
    def method(self):
        return super().method()


c2 = Child2(42)
result = c2.method()
assert result == 42, result
```

But when "Parent" is "dict" and method is "__iter__", that is not the case:

```
SHOW_BUG = True

class ChildDict1(dict):
    """Simplification of werkzeug.datastructures.MultiDict."""
    def __init__(self):
        pass
    
    if not SHOW_BUG:
        def __iter__(self):
            return super().__iter__()

    def add(self, key, value):
        self.setdefault(key, []).append(value)
    
    def __setitem__(self, key, value):
        """Like add, but removes any existing key first."""
        super().__setitem__(key, [value])
    
    def getall(self, key) -> list:
        return super().__getitem__(key)

    def __getitem__(self, key):
        """Return the first value for this key."""
        return self.getall(key)[0]

    def items(self, multi=False):
        for (key, values) in super().items():
            if multi:
                yield from ((key, value) for value in values)
            else:
                yield key, values[0]
    
    def values(self):
        return (values[0] for values in super().values())
    
    # Remaining overridden implementations of methods
    # inherited from dict are elided for brevity.


cd1 = ChildDict1()
assert dict(cd1) == {}
cd1[1] = "one"
cd1.add(1, "uno")
assert cd1.getall(1) == ["one", "uno"]
assert list(cd1.items()) == [(1, "one")]
assert list(cd1.values()) == [ "one"]
assert dict(cd1) == {1: "one"}, cd1  # XXX
```

If you run the above as-is, the line marked "XXX" will trigger an AssertionError demonstrating the unexpected behavior. If you change SHOW_BUG to False, it won’t.

Is it intended that toggling the value of SHOW_BUG in this code causes different results?

You can visit https://repl.it/@jab/dict-subclass-copy-surprise to run the examples above directly in your browser.
History
Date User Action Args
2021-02-17 22:12:33jabsetrecipients: + jab
2021-02-17 22:12:32jabsetmessageid: <1613599952.95.0.0689652491879.issue43246@roundup.psfhosted.org>
2021-02-17 22:12:32jablinkissue43246 messages
2021-02-17 22:12:32jabcreate