Я пытаюсь создать класс для представления списка именованных кортежей, и у меня возникают проблемы с доступом к элементам по имени. Вот пример:
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'), поэтому индекс известен в моем методе, но я не могу до него добраться. Кто-нибудь знает, как я могу получить доступ к этому значению, чтобы передать имя методу? Я пытаюсь избежать передачи имени в виде строки — мне бы очень хотелось воспользоваться пространством имен, поскольку в конце концов это одно из преимуществ именованного кортежа.






Интересный вопрос. Поле представляет собой дескриптор , поэтому вы можете вызвать его:
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
r.age примерно эквивалентен Record.age.__get__(r), с той лишь разницей, что мы передаем «экземпляр» r явно.
Итак, что касается подсказок типов, я думаю, я мог бы это сделать? def attempt_access(self, row: int, column: int|Callable) -> Any:
@MikeP Да, нет, мы не вызываем дескриптор, и его в любом случае нельзя вызвать. Тип-хинтинг дескриптора, вероятно, достоин нового вопроса, а не разъяснений в комментариях.
ОК — stackoverflow.com/questions/78451100/…
Это действительно полезно. Об этом есть раздел в книге «Python in a Nutshell» (стр. 119–120), но я думаю, мне придется прочитать его около 10 раз, чтобы понять.