如何在不使用字符串的情况下将namedtuple属性传递给方法?

问题描述 投票:0回答:2

我正在尝试创建一个类来表示命名元组的列表,但在按名称访问元素时遇到问题。这是一个例子:

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

因此我可以通过索引成功访问“行”和“列”,但是如果我想通过方法调用访问列(即namedtuple属性),则会收到错误。有趣的是,列值是

_tuplegetter(2, 'Alias for field number 2')
,因此索引在我的方法中是已知的,但我无法访问它。有谁知道如何访问该值以便将名称传递给该方法?我试图避免将名称作为字符串传递 - 我真的很想利用命名空间,因为这毕竟是命名元组的优点之一。

python
2个回答
3
投票

有趣的问题。该字段是一个描述符,因此您可以调用它:

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

0
投票

您需要将

attempt_access
修改为:

def attempt_access(self, row, column_name):
    r = self._data[row]
    if isinstance(column_name, int):
        print(f'{r[column_name]=}')
    else:
        column_index = r._fields.index(column_name)
        print(f'{r[column_index]=}')

然后调用:

class_data.attempt_access(0, 'age')

问题是在命名元组中使用整数。

结果输出:

show data
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
r[column_name]=30

attempt_access by name
r[column_index]=30
© www.soinside.com 2019 - 2024. All rights reserved.