
On Tue, 20 Dec 2022 at 11:16, Steven D'Aprano <steve@pearwood.info> wrote:
Speaking of dicts, the dict.fromkeys method cooperates with subclasses. That proves that it can be done from a builtin. True, it is a classmethod rather than an instance method, but any instance method can find out its own class by calling `type()` (or the internal, C equivalent) on `self`. Just as we can do from Python.
What you really mean here is that fromkeys cooperates with subclasses *that do not change the signature of __init__*. Otherwise, it won't work. The reason this is much easier with a classmethod alternate constructor is that, if you don't want that behaviour, just don't do that.
class NumDict(dict): ... def __init__(self, max, /): ... for i in range(max): self[i] = i ... NumDict(10) {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} NumDict(5) {0: 0, 1: 1, 2: 2, 3: 3, 4: 4} NumDict.fromkeys("abc") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: NumDict.__init__() missing 1 required positional argument: 'max'
So? Just don't use NumDict.fromkeys(), it doesn't make sense for a NumDict. And that's fine. But if something else didn't work, it would cause major problems. Which is why instance methods return plain dictionaries:
type(NumDict(5) | NumDict(3)) <class 'dict'>
How is the vanilla dictionary supposed to know how to construct a NumDict correctly? Come to think of it, how is dict supposed to know how to construct a defaultdict? Oh, it doesn't really.
d = defaultdict.fromkeys("asdf", 42) d["a"] 42 d["b"] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'b' d defaultdict(None, {'a': 42, 's': 42, 'd': 42, 'f': 42})
All it does is construct a vanilla dictionary, because that's all it knows how to do. If the rule is "all operations return an object of the subclass automatically", then the corollary is "all subclasses must retain the signature of the superclass". ChrisA