4

The namedtuple implementation in Python 2.7 implements __dict__. I'm confused what this is doing; why do we need to make a special __dict__ if there are already properties defined?

C:\tmp> python
Python 2.7.12 |Anaconda 4.1.1 (64-bit)| (default, Jun 29 2016, 11:07:13) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://anaconda.org
>>> import collections
>>> x = collections.namedtuple('foo','bar baz', verbose=True)
class foo(tuple):
    'foo(bar, baz)'

    __slots__ = ()

    _fields = ('bar', 'baz')

    def __new__(_cls, bar, baz):
        'Create new instance of foo(bar, baz)'
        return _tuple.__new__(_cls, (bar, baz))

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new foo object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != 2:
            raise TypeError('Expected 2 arguments, got %d' % len(result))
        return result

    def __repr__(self):
        'Return a nicely formatted representation string'
        return 'foo(bar=%r, baz=%r)' % self

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values'
        return OrderedDict(zip(self._fields, self))

    def _replace(_self, **kwds):
        'Return a new foo object replacing specified fields with new values'
        result = _self._make(map(kwds.pop, ('bar', 'baz'), _self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % kwds.keys())
        return result

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)

    __dict__ = _property(_asdict)

    def __getstate__(self):
        'Exclude the OrderedDict from pickling'
        pass

    bar = _property(_itemgetter(0), doc='Alias for field number 0')

    baz = _property(_itemgetter(1), doc='Alias for field number 1')
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • Because it has `__slots__`; it wouldn't have a `__dict__` if it didn't explicitly define one. – jonrsharpe Feb 02 '17 at 23:29
  • why does it need a `__dict__`? (what feature consumes it?) – Jason S Feb 02 '17 at 23:30
  • 3
    The use of ``__dict__`` was a mistake. The goal was to make ``vars(nt)`` work, but it had unexpected side-effects on pickling and was completely unworkable in Python 3. As a result, it was removed entirely from later versions of Python 3. Stick with an ``_asdict`` method and you will be happy :-) – Raymond Hettinger Feb 03 '17 at 01:23

1 Answers1

3

They just thought it would be convenient. Turns out it was more hassle than it was worth, causing issues with pickling and with subclasses, and it was conceptually confusing anyway. More recent Python versions don't have it any more.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • so if I'm doing something similar to `namedtuple`, can I just skip the `__dict__` assignment? If I do, what feature would I miss? – Jason S Feb 02 '17 at 23:31
  • 1
    @JasonS: You can skip it. It never provided enough benefit to justify the confusion. The obvious thing you'll be missing is that `yourobject.__dict__` will raise an AttributeError, but `yourobject._asdict()` is a better way to get that functionality. – user2357112 Feb 02 '17 at 23:33
  • does that apply to the `__getstate__` method also? – Jason S Feb 02 '17 at 23:43
  • @JasonS: You can leave that out too. It might be worth looking at the Python 3.6 namedtuple code, which is mostly compatible with Python 2. – user2357112 Feb 02 '17 at 23:46
  • 2
    If you want to know about the gory details of why `__dict__` appeared and disappeared, it was a muddle about getting `vars` to work on namedtuple instances. See [here](http://stackoverflow.com/a/26180604/674039) for more info. – wim Feb 03 '17 at 00:35