如何使用字段名称的变量访问命名元组的字段?

Posted

技术标签:

【中文标题】如何使用字段名称的变量访问命名元组的字段?【英文标题】:How to access a field of a namedtuple using a variable for the field name? 【发布时间】:2017-11-21 22:19:25 【问题描述】:

我可以按如下方式访问命名元组的元素(*):

from collections import namedtuple
Car = namedtuple('Car', 'color mileage')
my_car = Car('red', 100)
print my_car.color

但是如何使用变量来指定我要访问的字段的名称?例如

field = 'color'
my_car[field] # doesn't work
my_car.field # doesn't work

我的实际用例是我使用for row in data.itertuples() 遍历熊猫数据框。我正在对特定列中的值进行操作,并且我希望能够通过名称指定要使用的列作为包含此循环的方法的参数。

(*)example taken from here。我正在使用 Python 2.7。

【问题讨论】:

getattr(my_car, field)my_car._asdict()[field]. 也可以试试mycar[field],但是你可以使用for row in data进行迭代。 【参考方案1】:

您可以使用getattr

getattr(my_car, field)

【讨论】:

【参考方案2】:

“getattr”答案有效,但还有另一种稍快的选项。

idx = name: i for i, name in enumerate(list(df), start=1)
for row in df.itertuples(name=None):
   example_value = row[idx['product_price']]

说明

制作一个将列名映射到行位置的字典。用“name=None”调用“itertuples”。然后使用 使用字典中的列名获得的索引。

    制作字典以查找索引。

idx = name: i for i, name in enumerate(list(df), start=1)

    使用字典在行元组中按名称访问所需的值
for row in df.itertuples(name=None):
   example_value = row[idx['product_price']]

注意:如果您使用 index=False 调用 itertuples,请在 enumerate 中使用 start=0

这是一个工作示例,显示了这两种方法以及两种方法的时间安排。

import numpy as np
import pandas as pd
import timeit

data_length = 3 * 10**5
fake_data = 
    "id_code": list(range(data_length)),
    "letter_code": np.random.choice(list('abcdefgz'), size=data_length),
    "pine_cones": np.random.randint(low=1, high=100, size=data_length),
    "area": np.random.randint(low=1, high=100, size=data_length),
    "temperature": np.random.randint(low=1, high=100, size=data_length),
    "elevation": np.random.randint(low=1, high=100, size=data_length),

df = pd.DataFrame(fake_data)


def iter_with_idx():
    result_data = []
    
    idx = name: i for i, name in enumerate(list(df), start=1)
    
    for row in df.itertuples(name=None):
        
        row_calc = row[idx['pine_cones']] / row[idx['area']]
        result_data.append(row_calc)
        
    return result_data

      
def iter_with_getaatr():
    
    result_data = []
    for row in df.itertuples():
        row_calc = getattr(row, 'pine_cones') / getattr(row, 'area')
        result_data.append(row_calc)
        
    return result_data
    

dict_idx_method = timeit.timeit(iter_with_idx, number=100)
get_attr_method = timeit.timeit(iter_with_getaatr, number=100)

print(f'Dictionary index Method dict_idx_method:0.4f seconds')
print(f'Get attribute method get_attr_method:0.4f seconds')

结果:

Dictionary index Method 49.1814 seconds
Get attribute method 80.1912 seconds

我认为差异是由于创建元组与命名元组的开销较低,并且通过索引而不是 getattr 访问它的开销也较低,但这两者都只是猜测。如果有人知道更好,请发表评论。

我还没有研究过列数与行数对计时结果的影响。

【讨论】:

【参考方案3】:

由于python 3.6 版可以继承自typing.NamedTuple

import typing as tp


class HistoryItem(tp.NamedTuple):
    inp: str
    tsb: float
    rtn: int
    frequency: int = None

    def __getitem__(self, item):
        if isinstance(item, int):
            item = self._fields[item]
        return getattr(self, item)

    def get(self, item, default=None):
        try:
            return self[item]
        except (KeyError, AttributeError, IndexError):
            return default


item = HistoryItem("inp", 10, 10, 10)

print(item[0])  # 'inp'
print(item["inp"])  # 'inp'

【讨论】:

这需要被认为是现代答案。 Python 3.6 不需要这个,你可以使用collections.namedtuple 类工厂来实现同样的事情。 另外,tp.NamedTuple.__getitem__ 会给你一个属性错误。 tp.NamedTuple 不是真正的类……它有点丑,实际上,它只是用作元类魔法的工具,最终调用 collections.namedtuple 以返回从 tuple 派生的类。 @juanpa.arrivillaga 感谢您的指出。我已经修好了。【参考方案4】:

访问它们的另一种方式是:

field_idx = my_car._fields.index(field)
my_car[field_idx]

提取字段的索引,然后用它来索引命名元组。

【讨论】:

【参考方案5】:

使用以下代码

for i,x in enumerate(my_car._fields):
    print(x, my_car[i])

【讨论】:

以上是关于如何使用字段名称的变量访问命名元组的字段?的主要内容,如果未能解决你的问题,请参考以下文章

使用变量访问元组的值

python学习--为元组中每个元素命名

如何创建一个命名元组的 __repr__?

Pythonic 按字段名称对命名元组列表进行排序的方法

如何使用 pymongo 在 mongodb 中重命名父字段名称和嵌套字段值?

swift元组_08_swift元组基本使用