Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass a namedtuple attribute to a method without using a string?

Tags:

python

I'm trying to create a class to represent a list of named tuples and I'm having trouble with accessing elements by name. Here's an example:

from typing import NamedTuple


class Record(NamedTuple):
    id: int
    name: str
    age: int


class NamedTupleList:

    def __init__(self, data):
        self._data = data

    def attempt_access(self, row, column):
        print(f'{self._data[row].age=}')
        r = self._data[row]
        print(f'{type(column)=}')
        print(f'{column=}')
        print(f'{r[column]=}')

data = [Record(1, 'Bob', 30),
        Record(2, 'Carol', 25),
        Record(3, 'Ted', 29),
        Record(4, 'Alice', 28),
        ]
class_data = NamedTupleList(data)

print('show data')
print(f'{data[0]=}')
print(f'{type(data[0])=}')
print(f'{data[0].age=}')

print('\nshow class_data')
print(f'{type(class_data)=}')

print('\nattempt_access by index')
class_data.attempt_access(0, 2)

print('\nattempt_access by name')
class_data.attempt_access(0, Record.age)  # why can't I do this?

Produces:

data[0]=Record(id=1, name='Bob', age=30)
type(data[0])=<class '__main__.Record'>
data[0].age=30

show class_data
type(class_data)=<class '__main__.NamedTupleList'>

attempt_access by index
self._data[row].age=30
type(column)=<class 'int'>
column=2
r[column]=30

attempt_access by name
self._data[row].age=30
type(column)=<class '_collections._tuplegetter'>
column=_tuplegetter(2, 'Alias for field number 2')

    print(f'{r[column]=}')
             ~^^^^^^^^
TypeError: tuple indices must be integers or slices, not _collections._tuplegetter

So I can successfully access 'rows' and 'columns' by index, but if I want to access a column (i.e. namedtuple attribute) through a method call I get an error. What's interesting is that the column value is _tuplegetter(2, 'Alias for field number 2') so the index is known in my method but I can't get to it. Does anyone know how I can access this value so that I can pass a name to the method? I'm trying to avoid passing the name as a string - I'd really like to take advantage of the namespace since that's one of the advantages of a namedtuple after all.

like image 900
MikeP Avatar asked Sep 07 '25 19:09

MikeP


1 Answers

Interesting question. The field is a descriptor, so you can invoke it:

class NamedTupleList:

    def __init__(self, data):
        self._data = data

    def attempt_access(self, row, column):
        r = self._data[row]
        try:
            val = r[column]
        except TypeError:
            val = column.__get__(r)
        return val

Demo:

>>> class_data.attempt_access(0, 2)
30
>>> class_data.attempt_access(0, Record.age)
30
like image 117
wim Avatar answered Sep 09 '25 14:09

wim