Как передать атрибут Nametuple методу без использования строки?

Я пытаюсь создать класс для представления списка именованных кортежей, и у меня возникают проблемы с доступом к элементам по имени. Вот пример:

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?

Производит:

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

Таким образом, я могу успешно получить доступ к «строкам» и «столбцам» по индексу, но если я хочу получить доступ к столбцу (т. е. атрибуту Nametuple) через вызов метода, я получаю ошибку. Что интересно, значение столбца равно _tuplegetter(2, 'Alias for field number 2'), поэтому индекс известен в моем методе, но я не могу до него добраться. Кто-нибудь знает, как я могу получить доступ к этому значению, чтобы передать имя методу? Я пытаюсь избежать передачи имени в виде строки — мне бы очень хотелось воспользоваться пространством имен, поскольку в конце концов это одно из преимуществ именованного кортежа.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
0
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Интересный вопрос. Поле представляет собой дескриптор , поэтому вы можете вызвать его:

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

Демо:

>>> class_data.attempt_access(0, 2)
30
>>> class_data.attempt_access(0, Record.age)
30

Это действительно полезно. Об этом есть раздел в книге «Python in a Nutshell» (стр. 119–120), но я думаю, мне придется прочитать его около 10 раз, чтобы понять.

MikeP 08.05.2024 21:58
3.3.2.2. Реализация дескрипторов и 3.3.2.3. Вызов дескрипторов также может быть полезным справочником. Это тот же протокол, по которому доступ к атрибутам обычно работает с экземплярами именованных кортежей, т. е. r.age примерно эквивалентен Record.age.__get__(r), с той лишь разницей, что мы передаем «экземпляр» r явно.
wim 08.05.2024 22:19

Итак, что касается подсказок типов, я думаю, я мог бы это сделать? def attempt_access(self, row: int, column: int|Callable) -> Any:

MikeP 08.05.2024 22:35

@MikeP Да, нет, мы не вызываем дескриптор, и его в любом случае нельзя вызвать. Тип-хинтинг дескриптора, вероятно, достоин нового вопроса, а не разъяснений в комментариях.

wim 08.05.2024 22:40

ОК — stackoverflow.com/questions/78451100/…

MikeP 08.05.2024 23:01

Другие вопросы по теме