Namedtuples: some unexpected inconveniences

Peter Otten __peter__ at web.de
Thu Apr 13 03:17:28 EDT 2017


Deborah Swanson wrote:

> Peter Otten wrote, on Wednesday, April 12, 2017 11:35 PM
>> 
>> Deborah Swanson wrote:
>> 
>> > It's a small point, but I suspect getattr(record, label)
>> would still
>> > fail, even if label's value is 'label' and only 'label', but what's
>> > the point of having a variable if it will only ever have just one
>> > value?
>> 
>> You are misunderstanding. Your getattr() call fails because you have
>> 
>> label = "label"
>> 
>> burried somewhere in your code. As soon as you change that to
>> 
>> label = <insert an existing attribute name here>
>> 
>> the error will go away.
> 
> 
> Yes, the error goes away, but now getattr(record, label) is useless for
> processing field names, unless you want to write a line of code for each
> one. (I have 17 field names, and forget about passing label to a
> function.)

No, it's not useless:

>>> from collections import namedtuple
>>> T = namedtuple("T", "foo bar baz")
>>> t = T(1, 2, 3)
>>> for name in t._fields:
...     print(name, "=", getattr(t, name))
... 
foo = 1
bar = 2
baz = 3

And as a special service here's a mutable datatype with sufficient 
namedtuple compatibility to replicate the above snippet:

$ cat namedtuple_replacement.py
def struct(name, wanted_columns):
    class Struct:
        _fields = __slots__ = wanted_columns.split()

        def __init__(self, *args):
            names = self.__slots__
            if len(args) != len(names):
                raise ValueError
            for name, value in zip(names, args):
                setattr(self, name, value)

        @classmethod
        def _make(cls, args):
            return cls(*args)

        def __repr__(self):
            names = self.__slots__
            return "{}({})".format(
                self.__class__.__name__,
                ", ".join("{}={!r}".format(n, getattr(self, n)) for n in 
names)
            )

    Struct.__name__ = name
    return Struct

T = struct("T", "foo bar baz")
t = T(1, 2, 3)
print(t)
for name in t._fields:
    print(name, "=", getattr(t, name))
t.bar = 42
print(t)
$ python3 namedtuple_replacement.py 
T(foo=1, bar=2, baz=3)
foo = 1
bar = 2
baz = 3
T(foo=1, bar=42, baz=3)




More information about the Python-list mailing list