57

I'd like to use namedtuples internally, but I want to preserve compatibility with users that feed me ordinary tuples.

from collections import namedtuple

tuple_pi = (1, 3.14, "pi")  #Normal tuple 

Record = namedtuple("Record", ["ID", "Value", "Name"])

named_e = Record(2, 2.79, "e")  #Named tuple

named_pi = Record(tuple_pi)  #Error
TypeError: __new__() missing 2 required positional arguments: 'Value' and 'Name'

tuple_pi.__class__ = Record
TypeError: __class__ assignment: only for heap types
Christopher Peisert
  • 21,862
  • 3
  • 86
  • 117
Adam Ryczkowski
  • 7,592
  • 13
  • 42
  • 68

1 Answers1

81

You can use the *args call syntax:

named_pi = Record(*tuple_pi)

This passes in each element of the tuple_pi sequence as a separate argument.

You can also use the namedtuple._make() class method to turn any sequence into an instance:

named_pi = Record._make(tuple_pi)

Demo:

>>> from collections import namedtuple
>>> Record = namedtuple("Record", ["ID", "Value", "Name"])
>>> tuple_pi = (1, 3.14, "pi")
>>> Record(*tuple_pi)
Record(ID=1, Value=3.14, Name='pi')
>>> Record._make(tuple_pi)
Record(ID=1, Value=3.14, Name='pi')
Christopher Peisert
  • 21,862
  • 3
  • 86
  • 117
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 2
    Short and to-the-point. I cannot understand how did you manage to answer the question so quickly. – Adam Ryczkowski Jul 28 '14 at 16:51
  • Nice. You can also use the keyword args syntax to hand over a dict: MyNamedTuple(**mydict). Of course the dict needs to contain the tuple fields as keys. – jurgispods May 11 '16 at 07:30
  • W.r.t. to memory consumption: I can get rid of `tuplePi` after converting it with `namedPi = Record(*tuplePi)` or with `namedPi = Record._make(tuplePi)`, right? – thinwybk Apr 13 '18 at 14:37
  • @thinwybk: yes, the namedtuple instance now references the same values contained, so you don't need `tuplePi` anymore. – Martijn Pieters Apr 13 '18 at 17:12
  • `tuple.__new__(Record, tuple_pi)` is what `_make` [uses](https://github.com/python/cpython/blob/0729694246174a5c2f0ae197f2e0dbea61b90c9f/Lib/collections/__init__.py#L403-L422) under the hood. – ofo Jun 16 '21 at 12:38
  • @ofo: yes, because `Record.__new__` is itself a paper-thin wrapper around `tuple.__new__(Record, ...)`. It is simpler and more efficient to then re-use that same `tuple_new` reference than it is to use `cls.__new__(cls, iterable)`, which then creates a new function frame in the interpreter call stack before calling `tuple_new()` anyway. – Martijn Pieters Jun 16 '21 at 20:21