228

I have a named tuple class in python

class Town(collections.namedtuple('Town', [
    'name', 
    'population',
    'coordinates',
    'population', 
    'capital', 
    'state_bird'])):
    # ...

I'd like to convert Town instances into dictionaries. I don't want it to be rigidly tied to the names or number of the fields in a Town.

Is there a way to write it such that I could add more fields, or pass an entirely different named tuple in and get a dictionary.

I can not alter the original class definition as its in someone else's code. So I need to take an instance of a Town and convert it to a dictionary.

codeforester
  • 39,467
  • 16
  • 112
  • 140
Without Me It Just Aweso
  • 4,593
  • 10
  • 35
  • 53
  • 3
    btw... look at tab completion or the `dir` command, which will show you the fields for any object... that would have shown the `_asdict` function directly. – Corley Brigman Oct 03 '14 at 14:13
  • it looks like what you _really_ want to do is subclass from `dict` instead of 'namedtuple', and pass the namedtuple into the initializer. Remember that if you're used to Cxx, `class Town(x)` isn't the constructor, `def __init__(self, *args, **kwargs)` inside it is. – Corley Brigman Oct 03 '14 at 14:16
  • I can not alter the original class as its in someone elses code. so I have to subclass from namedtouble – Without Me It Just Aweso Oct 03 '14 at 14:36
  • @CorleyBrigman can you explain this more? I tried to find documentation on the named touple, or find what I could call on it and I couldnt figure out how. (Again python is not my strongest language) – Without Me It Just Aweso Oct 06 '14 at 20:04
  • 2
    which part? `dir` is just a python built-in... you can run it on _any_ python object, in a console or in a script (where it returns a list you can print or do whatever with), and it will return a list of (nearly) all the attributes the object. helpful if you're trying to figure out how an unknown object works. – Corley Brigman Oct 07 '14 at 13:49
  • I had no idea, thats is incredibly helpful. My biggest struggle with python is the lack of API documentation. I never know what I can do with objects, this is exactly what I needed! – Without Me It Just Aweso Oct 13 '14 at 16:46
  • For going the other way (dictionary into namedtuple), look here: https://stackoverflow.com/q/43921240/674039 – wim Oct 01 '20 at 16:06

6 Answers6

402

TL;DR: there's a method _asdict provided for this.

Here is a demonstration of the usage:

>>> from collections import namedtuple
>>> fields = ['name', 'population', 'coordinates', 'capital', 'state_bird']
>>> Town = namedtuple('Town', fields)
>>> funkytown = Town('funky', 300, 'somewhere', 'lipps', 'chicken')
>>> funkytown._asdict()
{'name': 'funky',
 'population': 300,
 'coordinates': 'somewhere',
 'capital': 'lipps',
 'state_bird': 'chicken'}

This is a documented method of namedtuples, i.e. unlike the usual convention in python the leading underscore on the method name isn't there to discourage use. Along with the other methods added to namedtuples, _make, _replace, _source, _fields, it has the underscore only to try and prevent conflicts with possible field names.


Note: For some 2.7.5 < python version < 3.5.0 code out in the wild, you might see this version:

>>> vars(funkytown)
OrderedDict([('name', 'funky'),
             ('population', 300),
             ('coordinates', 'somewhere'),
             ('capital', 'lipps'),
             ('state_bird', 'chicken')])

For a while the documentation had mentioned that _asdict was obsolete (see here), and suggested to use the built-in method vars. That advice is now outdated; in order to fix a bug related to subclassing, the __dict__ property which was present on namedtuples has again been removed by this commit.

wim
  • 338,267
  • 99
  • 616
  • 750
  • 2
    Does anyone know if there an established precedent that suggests `_asdict` shouldn't be aliased in the standard library to `asdict`? – KobeJohn May 23 '16 at 06:35
  • 5
    @KobeJohn then you couldn't have `"asdict"` be one of the tuple names. – shadowtalker Oct 16 '16 at 16:14
  • infuriating! `vars` is the natural, idiomatic way to do this! Could it not handle namedtuple as a special case? Doesn't seem to me like it would be tooo hard – robert Aug 03 '22 at 17:20
  • @robert `vars(obj)` is supposed to be equivalent to `obj.__dict__`, so the special-case would be changing the documented behavior of `vars` (tuple instances don't carry around a `__dict__`) – wim Aug 03 '22 at 18:23
  • yes, I understand the excuse, but it doesn't cut any ice with me. What you are describing is how it is implemented, and `vars` "hides" that implementation (to use the OO parlance). I guess there's probably a "good" reason why they couldn't simply overload the method, but to me it seems like a slip in standards. – robert Aug 03 '22 at 18:25
  • As of 3.8, `_asdict()` returns a `dict` and not an `OrderedDict` since as of version 3.7, `dict`s are ordered. See https://docs.python.org/3/library/collections.html#collections.namedtuple – Marc Meketon Mar 05 '23 at 18:45
35

There's a built in method on namedtuple instances for this, _asdict.

As discussed in the comments, on some versions vars() will also do it, but it's apparently highly dependent on build details, whereas _asdict should be reliable. In some versions _asdict was marked as deprecated, but comments indicate that this is no longer the case as of 3.4.

Peter DeGlopper
  • 36,326
  • 7
  • 90
  • 83
  • 1
    I wasn't the downvoter, but it could be because the `_asdict` method has been [obsoleted in python3 (in favour of vars)](https://docs.python.org/3/library/collections.html#collections.somenamedtuple._asdict) – wim Oct 03 '14 at 14:16
  • Conversely, it looks like `vars` does't work on some older versions - on 2.7 it raises a `TypeError`, since that version's `namedtuple` class does not have an `__dict__` attribute. – Peter DeGlopper Oct 03 '14 at 14:17
  • yes, Martijn and I have discussed that [here](http://stackoverflow.com/a/19840533/674039). It will work on newer versions of 2.7 btw (I'm on 2.7.6 and it works) – wim Oct 03 '14 at 14:18
  • Past the edit window on my above comment - it fails on 2.7.5 so it must be new as of 2.7.6. Unless my 2.7.5 build is off, as Martijn's was at the linked answer? Anyway, it seems like whether or not it works on 2.7.5 depends on build specifics. – Peter DeGlopper Oct 03 '14 at 14:23
  • 8
    Just a heads up: _asdict is not longer obseleted (and returns an OrderedDict now), and vars produces an error with Python 3.4 (from the removal of the __dict__ attribute of namedtuples). – Alex Huszagh Nov 21 '15 at 13:39
  • @AlexanderHuszagh - thanks, updated the answer with that information. Looks like `_asdict` is the most consistent way to do it, even if it was deprecated in some versions. – Peter DeGlopper Nov 21 '15 at 18:00
  • @AlexanderHuszagh That's inaccurate, it's still working in python 3.5.0. – wim Dec 08 '15 at 22:51
  • @wim, I said in 3.4. The `__dict__` magic method has been removed and inserted many times in various Python versions. The class does have it, however, the instance does not, meaning vars will not **reliably** work. _asdict was once deprecated, however, it is no longer and works in recent 2.7.x and 3.2+ versions, meaning it is the most reliable way to convert namedtuples to a mapping structure. – Alex Huszagh Dec 08 '15 at 23:34
  • Yes, I understand. But it is plainly incorrect that `vars()` is no longer working in 3.4 (as OP has edited into the answer). [Online python 3.4 example of it working here](https://ideone.com/gk6Kmj). – wim Dec 09 '15 at 00:45
  • 1
    I can't keep track of the apparently often-changing state of `vars()`. Open to suggestions for how else to accurately describe it. – Peter DeGlopper Dec 09 '15 at 01:33
  • So I just tested it on 5 different builds. 3.4.3+ on Ubuntu (GCC 5.2.1, installed via apt-get) fails, 3.5.1 on Ubuntu (GCC 5.2.1, installed via makefile) fails, Windows 3.4.1 installed by MSI succeeds, it works on Python 3.3.6 (GCC 5.2.1, Ubuntu, makefile), and works on Python 3.2.6 (Ubuntu, GCC 5.2.1, makefile). I would say it does **not** work fairly reliably, and that should be a key point. Python is meant to be **portable**, if it fails on half of all CPython builds, that's a very low standard for portability. This is why I say use _asdict. It's reliable. – Alex Huszagh Dec 09 '15 at 07:14
  • On all of the above, `_asdict` works. I get that this is highly build dependent, but considering that `_asdict` works, reliably, and also that `__dict__` in Python3.x is just a property of `_asdict` in the Python3.3 and Python3.2 methods (looked manually at the source code in collections/__init__.py). Since `__dict__` just is a property of `_asdict`, and is intermittently present and missing, shouldn't `_asdict` be the preferred method? (Line 274 in Python3.3.6, collections/__init__.py) – Alex Huszagh Dec 09 '15 at 07:21
8

Normally _asdict() returns a OrderedDict. this is how to convert from OrderedDict to a regular dict


town = Town('funky', 300, 'somewhere', 'lipps', 'chicken')
dict(town._asdict())

the output will be

{'capital': 'lipps',
 'coordinates': 'somewhere',
 'name': 'funky',
 'population': 300,
 'state_bird': 'chicken'}
Simone
  • 813
  • 8
  • 21
3

On Ubuntu 14.04 LTS versions of python2.7 and python3.4 the __dict__ property worked as expected. The _asdict method also worked, but I'm inclined to use the standards-defined, uniform, property api instead of the localized non-uniform api.

$ python2.7

# Works on:
# Python 2.7.6 (default, Jun 22 2015, 17:58:13)  [GCC 4.8.2] on linux2
# Python 3.4.3 (default, Oct 14 2015, 20:28:29)  [GCC 4.8.4] on linux

import collections

Color = collections.namedtuple('Color', ['r', 'g', 'b'])
red = Color(r=256, g=0, b=0)

# Access the namedtuple as a dict
print(red.__dict__['r'])  # 256

# Drop the namedtuple only keeping the dict
red = red.__dict__
print(red['r'])  #256

Seeing as dict is the semantic way to get a dictionary representing soemthing, (at least to the best of my knowledge).


It would be nice to accumulate a table of major python versions and platforms and their support for __dict__, currently I only have one platform version and two python versions as posted above.

| Platform                      | PyVer     | __dict__ | _asdict |
| --------------------------    | --------- | -------- | ------- |
| Ubuntu 14.04 LTS              | Python2.7 | yes      | yes     |
| Ubuntu 14.04 LTS              | Python3.4 | yes      | yes     |
| CentOS Linux release 7.4.1708 | Python2.7 | no       | yes     |
| CentOS Linux release 7.4.1708 | Python3.4 | no       | yes     |
| CentOS Linux release 7.4.1708 | Python3.6 | no       | yes     |
gbtimmon
  • 4,238
  • 1
  • 21
  • 36
ThorSummoner
  • 16,657
  • 15
  • 135
  • 147
  • 1
    Linux-3.10.0-693.el7.x86_64-x86_64-with-centos-7.4.1708-Core, Python 2.7 -- `__dict__` does not work. – gbtimmon Dec 20 '18 at 17:19
-1

Case #1: one dimension tuple

TUPLE_ROLES = (
    (912,"Role 21"),
    (913,"Role 22"),
    (925,"Role 23"),
    (918,"Role 24"),
)


TUPLE_ROLES[912]  #==> Error because it is out of bounce. 
TUPLE_ROLES[  2]  #==> will show Role 23.
DICT1_ROLE = {k:v for k, v in TUPLE_ROLES }
DICT1_ROLE[925] # will display "Role 23" 

Case #2: Two dimension tuple
Example: DICT_ROLES[961] # will show 'Back-End Programmer'

NAMEDTUPLE_ROLES = (
    ('Company', ( 
            ( 111, 'Owner/CEO/President'), 
            ( 113, 'Manager'),
            ( 115, 'Receptionist'),
            ( 117, 'Marketer'),
            ( 119, 'Sales Person'),
            ( 121, 'Accountant'),
            ( 123, 'Director'),
            ( 125, 'Vice President'),
            ( 127, 'HR Specialist'),
            ( 141, 'System Operator'),
    )),
    ('Restaurant', ( 
            ( 211, 'Chef'), 
            ( 212, 'Waiter/Waitress'), 
    )),
    ('Oil Collector', ( 
            ( 211, 'Truck Driver'), 
            ( 213, 'Tank Installer'), 
            ( 217, 'Welder'),
            ( 218, 'In-house Handler'),
            ( 219, 'Dispatcher'),
    )),
    ('Information Technology', ( 
            ( 912, 'Server Administrator'),
            ( 914, 'Graphic Designer'),
            ( 916, 'Project Manager'),
            ( 918, 'Consultant'),
            ( 921, 'Business Logic Analyzer'),
            ( 923, 'Data Model Designer'),
            ( 951, 'Programmer'),
            ( 953, 'WEB Front-End Programmer'),
            ( 955, 'Android Programmer'),
            ( 957, 'iOS Programmer'),
            ( 961, 'Back-End Programmer'),
            ( 962, 'Fullstack Programmer'),
            ( 971, 'System Architect'),
    )),
)

#Thus, we need dictionary/set

T4 = {}
def main():
    for k, v in NAMEDTUPLE_ROLES:
        for k1, v1 in v:
            T4.update ( {k1:v1}  )
    print (T4[961]) # will display 'Back-End Programmer'
    # print (T4) # will display all list of dictionary

main()
yongtaek jun
  • 125
  • 1
  • 3
-5

Python 3. Allocate any field to the dictionary as the required index for the dictionary, I used 'name'.

import collections

Town = collections.namedtuple("Town", "name population coordinates capital state_bird")

town_list = []

town_list.append(Town('Town 1', '10', '10.10', 'Capital 1', 'Turkey'))
town_list.append(Town('Town 2', '11', '11.11', 'Capital 2', 'Duck'))

town_dictionary = {t.name: t for t in town_list}
Andre Odendaal
  • 759
  • 7
  • 7