3

How do I initialize a namedtuple from a Namespace?

import collections
import argparse

nt=collections.namedtuple("nt",["foo","bar"]) # _NOT_ "baz"!
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
parser.add_argument('--bar')
parser.add_argument('--baz')
args = parser.parse_args(...)

What do I do if only some script arguments go into the namedtuple?

Here is what I came up with:

nt_param=nt(**{f:getattr(args,f,None) for f in nt._fields})

is there a more pythonic approach?

sds
  • 58,617
  • 29
  • 161
  • 278
  • 2
    "If all arguments of the program were in nt, I could have passed namespace=nt() to parser.parse_args()." except you couldn't because namedtuples are **immutable** – anthony sottile Jul 24 '17 at 17:00
  • @AnthonySottile: thanks, removed. – sds Jul 24 '17 at 17:06
  • A small improvement is to remove the `None` in your call to `getattr`. Since the `Namespace` object already sets any undefined command-line arguments to `None`, you don't have to explicitly return `None` yourself. – Christian Dean Jul 24 '17 at 17:11
  • https://stackoverflow.com/questions/34348568/can-i-convert-a-namespace-object-from-mutable-to-immutable - asks about converting a namespace to immutable. Besides copying to a `namedtuple` I suggest a custom `Namespace` class. I also discuss `namedtuple` in https://stackoverflow.com/questions/42279063/python-typehints-for-argparse-namespace-objects – hpaulj Jul 24 '17 at 17:51
  • https://pypi.python.org/pypi/recordtype is a mutable alternative to `namedtype`. – hpaulj Jul 24 '17 at 17:55
  • So the problem comes down to creating a `namedtuple` from a dictionary with a partial overlap in keys. You not only have to select the keys that match, but define default values for missing ones. You could use `parser.set_defaults` to handle the missing keys issue. – hpaulj Jul 24 '17 at 21:05

1 Answers1

0

If you don't care about the actual type of the namedtuple, you can create an "anonymous" namedtuple representing your Namespace.

Here's an approach I've used before:

def auto_namedtuple(classname='auto_namedtuple', **kwargs):
    return collections.namedtuple(classname, tuple(kwargs))(**kwargs)

Usage in your case:

nt = auto_namedtuple(**vars(args))
anthony sottile
  • 61,815
  • 15
  • 148
  • 207
  • It is important that the `namedtuple` contains only the specific fields. – sds Jul 24 '17 at 17:06
  • What you're doing seems fine, are you have a specific *problem*? – anthony sottile Jul 24 '17 at 17:07
  • Not really, I am just trying to see if I am doing something stupid. Thanks for your help! – sds Jul 24 '17 at 17:25
  • Note that while `_fields` has a leading underscore, it is not actually a "private" attribute, the stdlib does this [to avoid conflicting with user namespaces](https://docs.python.org/3/library/collections.html#namedtuple-factory-function-for-tuples-with-named-fields) – anthony sottile Jul 24 '17 at 17:30