1

For most, I am not sure if it's the right question to be asked but I couldn't yet found out why there are two different types of named tuple... I have read " What's the difference between namedtuple and NamedTuple?" page. However, I still don't understand how to convert a dictionary to a NamedTuple. I have tried this code :

from collections import namedtuple
def convert(dictionary):
    return namedtuple('GenericDict', dictionary.keys())(**dictionary)

however, this piece of code only converts the dict to a namedtuple from the collection module.

I was wondering if anyone can help me out on this. How should I make a function to transform any random dict into a typing.NamedTuple.

Assume we have a class of NamedTuple like this :


class settingdefault(NamedTuple):
    epoch : int = 8
    train_size : float = 0.8
    b: str = "doe"

and we just want to get an input of dict from the user and transform it to NamedTuple. So if there was an element missing it can get replaced by the settingdefault class.

and lets assume that the example dict is :

config = dict(e=10, b="test")

BTW, I want it to be like a function. other than that I know how to do it like :


setting = settingdefault(config['a'], config['b'])

I want to be able to have it for cases that I don't know the keys of the coming config dict as it can be anything. Once again for the clarification ! My question is about typing.NamedTuple not the collections.namedtuple .

kimiya_Ab
  • 21
  • 1
  • 3
  • Possible duplicate of [Pythonic Way to Convert Dictionary to namedtuple, or Another Hashable dict-like?](https://stackoverflow.com/questions/43921240/pythonic-way-to-convert-dictionary-to-namedtuple-or-another-hashable-dict-like) – Matej Novosad Sep 11 '19 at 21:30
  • 1
    It is not a duplicate as it comes from different modules. – kimiya_Ab Sep 12 '19 at 19:49

1 Answers1

0

In this case, it's probably easiest to use typing's own metaclass for NamedTuple to create the class. The tricky part is that typing.NamedTuples need to have types associated for the fields.

Your conversion functions will end up looking something like this:

def convert(class_specs):
    field_types = {field: type(value) for field, value in class_specs.items()}
    return typing.NamedTupleMeta(
        'GenericDict', [], dict(class_specs, __annotations__=field_types))

and like your example you can use it as follows:

>>> Coordinates = convert({'x': 1, 'y': 23})
>>> Coordinates()
GenericDict(x=1, y=23)
>>> Coordinates(0, 0)
GenericDict(x=0, y=0)

Getting rid of the defaults

If you don't want to use the values from the dictionary as defaults, you can simply directly use the NamedTuple constructor like so:

def convert(class_specs):
    return typing.NamedTuple('GenericDict', 
        [(field, type(value)) for field, value in class_specs.items()])
>>> Coordinates = convert({'x': 42, 'y': 21})
>>> Coordinates()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __new__() missing 2 required positional arguments: 'x' and 'y'
>>> Coordinates(x=1, y=2)
GenericDict(x=1, y=2)

Getting rid of defaults and type information

If you don't want the defaults or typing information, you can simply switch it out for the generic typing.Any using:

def convert(class_specs):
    return typing.NamedTuple('GenericDict', 
        [(field, typing.Any) for field in class_specs.keys()])
Ammar Askar
  • 475
  • 3
  • 9
  • Thanks for the response. I just want to have new config instead of my defaults for the particular run time that the user is using, I don't want to change the default for the whole work (I have used NamedTuple for this matter instead of defaultdict because of this reason). for making a new instance from the class there is no '__new__ ' for this NamedTuple? – kimiya_Ab Sep 12 '19 at 16:44
  • `__new__` I meant – kimiya_Ab Sep 12 '19 at 16:49