Possible wrong behavior of the dict?
Hello! In order to explain, let define subclass of dict: class Pair: def __init__(self, key, val): self.key = key self.val = val class MyDict(dict): # def __init__(self, *args, **kwds): if len(args) > 1: raise TypeError('Expected at most 1 arguments, but got %d' % len(args)) for key, val in args[0]: self[key] = val for key, val in kwds.items(): self[key] = val def __getitem__(self, key): pair = dict.__getitem__(key) return pair.value def __setitem__(self, key, val): if key in self: pair = dict.__getitem__(key) pair.value = value else: pair = Pair(key, val) dict.__setitem__(self, key, pair) def values(self): for key in self: p = dict.__getitem__(self, key) yield p.value def items(self): for key, p in dict.__iter__(self): yield p.key, p.value The simple test give me strange result:
d = MyDict([('a', 1), ('b', 2), ('c', 3)]) dict(d) {'a': <__main__.Pair at 0x104ca9e48>, 'b': <__main__.Pair at 0x104ca9e80>, 'c': <__main__.Pair at 0x104ca9eb8>}
instead of {'a':1, 'b':2, 'c':3}. Is this right behavior of the dict? --- Zaur Shibzukhov
On Tue, Mar 17, 2015 at 3:05 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Hello!
In order to explain, let define subclass of dict:
class Pair: def __init__(self, key, val): self.key = key self.val = val
class MyDict(dict): # def __init__(self, *args, **kwds): if len(args) > 1: raise TypeError('Expected at most 1 arguments, but got %d' % len(args))
for key, val in args[0]: self[key] = val
for key, val in kwds.items(): self[key] = val
def __getitem__(self, key): pair = dict.__getitem__(key) return pair.value
def __setitem__(self, key, val): if key in self: pair = dict.__getitem__(key) pair.value = value else: pair = Pair(key, val) dict.__setitem__(self, key, pair)
def values(self): for key in self: p = dict.__getitem__(self, key) yield p.value
def items(self): for key, p in dict.__iter__(self): yield p.key, p.value
The simple test give me strange result:
d = MyDict([('a', 1), ('b', 2), ('c', 3)]) dict(d) {'a': <__main__.Pair at 0x104ca9e48>, 'b': <__main__.Pair at 0x104ca9e80>, 'c': <__main__.Pair at 0x104ca9eb8>}
instead of {'a':1, 'b':2, 'c':3}.
Is this right behavior of the dict?
Yes because in your __setitem__ call you are storing the value as the Pair. So when dict prints its repr it prints the key and value, and in this case the value is a Pair.
Yes... But I expected that dict constructor will use `__getitem__` or `items` method of MyDict instance in order to retrieve items of the MyDict instance during construction of the dict instance... Instead it interpreted MyDict instance as the dict instance during construction of new dict.This exactly caused my confusion. --- *Zaur Shibzukhov* 2015-03-17 22:12 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:05 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Hello!
In order to explain, let define subclass of dict:
class Pair: def __init__(self, key, val): self.key = key self.val = val
class MyDict(dict): # def __init__(self, *args, **kwds): if len(args) > 1: raise TypeError('Expected at most 1 arguments, but got %d' % len(args))
for key, val in args[0]: self[key] = val
for key, val in kwds.items(): self[key] = val
def __getitem__(self, key): pair = dict.__getitem__(key) return pair.value
def __setitem__(self, key, val): if key in self: pair = dict.__getitem__(key) pair.value = value else: pair = Pair(key, val) dict.__setitem__(self, key, pair)
def values(self): for key in self: p = dict.__getitem__(self, key) yield p.value
def items(self): for key, p in dict.__iter__(self): yield p.key, p.value
The simple test give me strange result:
d = MyDict([('a', 1), ('b', 2), ('c', 3)]) dict(d) {'a': <__main__.Pair at 0x104ca9e48>, 'b': <__main__.Pair at 0x104ca9e80>, 'c': <__main__.Pair at 0x104ca9eb8>}
instead of {'a':1, 'b':2, 'c':3}.
Is this right behavior of the dict?
Yes because in your __setitem__ call you are storing the value as the Pair. So when dict prints its repr it prints the key and value, and in this case the value is a Pair.
* Zaur Shibzukhov <szport@gmail.com> [2015-03-17 22:29:07 +0300]:
Yes... But I expected that dict constructor will use `__getitem__` or `items` method of MyDict instance in order to retrieve items of the MyDict instance during construction of the dict instance... Instead it interpreted MyDict instance as the dict instance during construction of new dict.This exactly caused my confusion.
Subclassing builtins is always a recipe for trouble, because the C implementation doesn't necessarily call your Python methods. You should probably use collections.UserDict or collections.abc.(Mutable)Mapping instead: https://docs.python.org/3/library/collections.html#collections.UserDict https://docs.python.org/3/library/collections.abc.html#collections.abc.Mappi... Florian -- http://www.the-compiler.org | me@the-compiler.org (Mail/XMPP) GPG: 916E B0C8 FD55 A072 | http://the-compiler.org/pubkey.asc I love long mails! | http://email.is-not-s.ms/
On Tue, Mar 17, 2015 at 3:29 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Yes... But I expected that dict constructor will use `__getitem__` or `items` method of MyDict instance in order to retrieve items of the MyDict instance during construction of the dict instance... Instead it interpreted MyDict instance as the dict instance during construction of new dict.This exactly caused my confusion.
It's because you subclassed dict. Copying is optimized to skip over using the methods you listed when the object is a dict and so we know the structure of the object at the C level. You can look at https://hg.python.org/cpython/file/22a0c925a7c2/Objects/dictobject.c#l1997 to see the actual code. -Brett
--- *Zaur Shibzukhov*
2015-03-17 22:12 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:05 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Hello!
In order to explain, let define subclass of dict:
class Pair: def __init__(self, key, val): self.key = key self.val = val
class MyDict(dict): # def __init__(self, *args, **kwds): if len(args) > 1: raise TypeError('Expected at most 1 arguments, but got %d' % len(args))
for key, val in args[0]: self[key] = val
for key, val in kwds.items(): self[key] = val
def __getitem__(self, key): pair = dict.__getitem__(key) return pair.value
def __setitem__(self, key, val): if key in self: pair = dict.__getitem__(key) pair.value = value else: pair = Pair(key, val) dict.__setitem__(self, key, pair)
def values(self): for key in self: p = dict.__getitem__(self, key) yield p.value
def items(self): for key, p in dict.__iter__(self): yield p.key, p.value
The simple test give me strange result:
d = MyDict([('a', 1), ('b', 2), ('c', 3)]) dict(d) {'a': <__main__.Pair at 0x104ca9e48>, 'b': <__main__.Pair at 0x104ca9e80>, 'c': <__main__.Pair at 0x104ca9eb8>}
instead of {'a':1, 'b':2, 'c':3}.
Is this right behavior of the dict?
Yes because in your __setitem__ call you are storing the value as the Pair. So when dict prints its repr it prints the key and value, and in this case the value is a Pair.
So in such cases it should not subclassed `dict`, but `collections.MutableMapping`, for example? --- *Zaur Shibzukhov* 2015-03-17 22:38 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:29 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Yes... But I expected that dict constructor will use `__getitem__` or `items` method of MyDict instance in order to retrieve items of the MyDict instance during construction of the dict instance... Instead it interpreted MyDict instance as the dict instance during construction of new dict.This exactly caused my confusion.
It's because you subclassed dict. Copying is optimized to skip over using the methods you listed when the object is a dict and so we know the structure of the object at the C level. You can look at https://hg.python.org/cpython/file/22a0c925a7c2/Objects/dictobject.c#l1997 to see the actual code.
-Brett
--- *Zaur Shibzukhov*
2015-03-17 22:12 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:05 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Hello!
In order to explain, let define subclass of dict:
class Pair: def __init__(self, key, val): self.key = key self.val = val
class MyDict(dict): # def __init__(self, *args, **kwds): if len(args) > 1: raise TypeError('Expected at most 1 arguments, but got %d' % len(args))
for key, val in args[0]: self[key] = val
for key, val in kwds.items(): self[key] = val
def __getitem__(self, key): pair = dict.__getitem__(key) return pair.value
def __setitem__(self, key, val): if key in self: pair = dict.__getitem__(key) pair.value = value else: pair = Pair(key, val) dict.__setitem__(self, key, pair)
def values(self): for key in self: p = dict.__getitem__(self, key) yield p.value
def items(self): for key, p in dict.__iter__(self): yield p.key, p.value
The simple test give me strange result:
> d = MyDict([('a', 1), ('b', 2), ('c', 3)]) > dict(d) {'a': <__main__.Pair at 0x104ca9e48>, 'b': <__main__.Pair at 0x104ca9e80>, 'c': <__main__.Pair at 0x104ca9eb8>}
instead of {'a':1, 'b':2, 'c':3}.
Is this right behavior of the dict?
Yes because in your __setitem__ call you are storing the value as the Pair. So when dict prints its repr it prints the key and value, and in this case the value is a Pair.
On Tue, Mar 17, 2015 at 3:46 PM Zaur Shibzukhov <szport@gmail.com> wrote:
So in such cases it should not subclassed `dict`, but `collections.MutableMapping`, for example?
Yes (see the comment at https://hg.python.org/cpython/file/22a0c925a7c2/Objects/dictobject.c#l2003 ). -Brett
--- *Zaur Shibzukhov*
2015-03-17 22:38 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:29 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Yes... But I expected that dict constructor will use `__getitem__` or `items` method of MyDict instance in order to retrieve items of the MyDict instance during construction of the dict instance... Instead it interpreted MyDict instance as the dict instance during construction of new dict.This exactly caused my confusion.
It's because you subclassed dict. Copying is optimized to skip over using the methods you listed when the object is a dict and so we know the structure of the object at the C level. You can look at https://hg.python.org/cpython/file/22a0c925a7c2/Objects/dictobject.c#l1997 to see the actual code.
-Brett
--- *Zaur Shibzukhov*
2015-03-17 22:12 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:05 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Hello!
In order to explain, let define subclass of dict:
class Pair: def __init__(self, key, val): self.key = key self.val = val
class MyDict(dict): # def __init__(self, *args, **kwds): if len(args) > 1: raise TypeError('Expected at most 1 arguments, but got %d' % len(args))
for key, val in args[0]: self[key] = val
for key, val in kwds.items(): self[key] = val
def __getitem__(self, key): pair = dict.__getitem__(key) return pair.value
def __setitem__(self, key, val): if key in self: pair = dict.__getitem__(key) pair.value = value else: pair = Pair(key, val) dict.__setitem__(self, key, pair)
def values(self): for key in self: p = dict.__getitem__(self, key) yield p.value
def items(self): for key, p in dict.__iter__(self): yield p.key, p.value
The simple test give me strange result:
>> d = MyDict([('a', 1), ('b', 2), ('c', 3)]) >> dict(d) {'a': <__main__.Pair at 0x104ca9e48>, 'b': <__main__.Pair at 0x104ca9e80>, 'c': <__main__.Pair at 0x104ca9eb8>}
instead of {'a':1, 'b':2, 'c':3}.
Is this right behavior of the dict?
Yes because in your __setitem__ call you are storing the value as the Pair. So when dict prints its repr it prints the key and value, and in this case the value is a Pair.
Thanks. --- *Zaur Shibzukhov* 2015-03-17 22:48 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:46 PM Zaur Shibzukhov <szport@gmail.com> wrote:
So in such cases it should not subclassed `dict`, but `collections.MutableMapping`, for example?
Yes (see the comment at https://hg.python.org/cpython/file/22a0c925a7c2/Objects/dictobject.c#l2003 ).
-Brett
--- *Zaur Shibzukhov*
2015-03-17 22:38 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:29 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Yes... But I expected that dict constructor will use `__getitem__` or `items` method of MyDict instance in order to retrieve items of the MyDict instance during construction of the dict instance... Instead it interpreted MyDict instance as the dict instance during construction of new dict.This exactly caused my confusion.
It's because you subclassed dict. Copying is optimized to skip over using the methods you listed when the object is a dict and so we know the structure of the object at the C level. You can look at https://hg.python.org/cpython/file/22a0c925a7c2/Objects/dictobject.c#l1997 to see the actual code.
-Brett
--- *Zaur Shibzukhov*
2015-03-17 22:12 GMT+03:00 Brett Cannon <brett@python.org>:
On Tue, Mar 17, 2015 at 3:05 PM Zaur Shibzukhov <szport@gmail.com> wrote:
Hello!
In order to explain, let define subclass of dict:
class Pair: def __init__(self, key, val): self.key = key self.val = val
class MyDict(dict): # def __init__(self, *args, **kwds): if len(args) > 1: raise TypeError('Expected at most 1 arguments, but got %d' % len(args))
for key, val in args[0]: self[key] = val
for key, val in kwds.items(): self[key] = val
def __getitem__(self, key): pair = dict.__getitem__(key) return pair.value
def __setitem__(self, key, val): if key in self: pair = dict.__getitem__(key) pair.value = value else: pair = Pair(key, val) dict.__setitem__(self, key, pair)
def values(self): for key in self: p = dict.__getitem__(self, key) yield p.value
def items(self): for key, p in dict.__iter__(self): yield p.key, p.value
The simple test give me strange result:
>>> d = MyDict([('a', 1), ('b', 2), ('c', 3)]) >>> dict(d) {'a': <__main__.Pair at 0x104ca9e48>, 'b': <__main__.Pair at 0x104ca9e80>, 'c': <__main__.Pair at 0x104ca9eb8>}
instead of {'a':1, 'b':2, 'c':3}.
Is this right behavior of the dict?
Yes because in your __setitem__ call you are storing the value as the Pair. So when dict prints its repr it prints the key and value, and in this case the value is a Pair.
participants (3)
-
Brett Cannon
-
Florian Bruhin
-
Zaur Shibzukhov