【问题标题】:Changing values of a list of namedtuples更改命名元组列表的值
【发布时间】:2015-09-24 00:26:37
【问题描述】:

我有一个名为 Books 的命名元组列表,我试图将 price 字段增加 20%,这确实会改变 Books 的值。我试着做:

from collections import namedtuple
Book = namedtuple('Book', 'author title genre year price instock')
BSI = [
       Book('Suzane Collins','The Hunger Games', 'Fiction', 2008, 6.96, 20),
       Book('J.K. Rowling', "Harry Potter and the Sorcerer's Stone", 'Fantasy', 1997, 4.78, 12)]
for item in BSI:
    item = item.price*1.10
print(item.price)

但我不断得到:

 Traceback (most recent call last):
 print(item.price)
 AttributeError: 'float' object has no attribute 'price'

我了解我无法在命名元组中设置字段。如何更新price

我试着把它变成一个函数:

def restaurant_change_price(rest, newprice):
    rest.price = rest._replace(price = rest.price + newprice)
    return rest.price

print(restaurant_change_price(Restaurant("Taillevent", "French", "343-3434", "Escargots", 24.50), 25))

但我得到一个错误替换说:

 rest.price = rest._replace(price = rest.price + newprice)
 AttributeError: can't set attribute

谁能告诉我为什么会这样?

【问题讨论】:

    标签: python list python-3.x tuples


    【解决方案1】:

    命名元组是不可变的,因此您无法操纵它们。

    正确的做法:

    如果你想要一些可变的,你可以使用recordtype

    from recordtype import recordtype
    
    Book = recordtype('Book', 'author title genre year price instock')
    books = [
       Book('Suzane Collins','The Hunger Games', 'Fiction', 2008, 6.96, 20),
       Book('J.K. Rowling', "Harry Potter and the Sorcerer's Stone", 'Fantasy', 1997, 4.78, 12)]
    
    for book in books:
        book.price *= 1.1
        print(book.price)
    

    PS:如果你没有安装它,你可能需要pip install recordtype

    不好的做法:

    您也可以继续使用namedtuple_replace() 方法。

    from collections import namedtuple
    
    Book = namedtuple('Book', 'author title genre year price instock')
    books = [
       Book('Suzane Collins','The Hunger Games', 'Fiction', 2008, 6.96, 20),
       Book('J.K. Rowling', "Harry Potter and the Sorcerer's Stone", 'Fantasy', 1997, 4.78, 12)]
    
    for i in range(len(books)):
        books[i] = books[i]._replace(price = books[i].price*1.1)
        print(books[i].price)
    

    【讨论】:

    • @LeonSurrao 我按照你的意愿用_replace() 方法更新了帖子。
    • 我不了解 python 社区...namedtuple._replace() 在函数式语言中非常惯用,为什么这里是“私有”!?这确实不应该是一个坏方法,它肯定比转换为可变的疯狂要好
    • rest = rest._replace(price = rest.price + newprice)代替rest.price = rest._replace(price = rest.price + newprice)。请注意,_replace() 会返回一个新的 namedtuple 实例。
    • @progo,_replace 实际上不是“私有”或内部的。基于this doc:为防止与字段名冲突,方法名和属性名以下划线开头。
    • @Sait “假设您的元组中有数百万个项目。”这不太可能发生,是吗?
    【解决方案2】:

    在 Python >= 3.7 中,您可以使用 dataclass 装饰器和新的变量注释功能来生成可变记录类型:

    from dataclasses import dataclass
    
    
    @dataclass
    class Book:
        author: str
        title: str
        genre: str
        year: int
        price: float
        instock: int
    
    
    BSI = [
        Book("Suzane Collins", "The Hunger Games", "Fiction", 2008, 6.96, 20),
        Book(
            "J.K. Rowling",
            "Harry Potter and the Sorcerer's Stone",
            "Fantasy",
            1997,
            4.78,
            12,
        ),
    ]
    
    for item in BSI:
        item.price *= 1.10
        print(f"New price for '{item.title}' book is {item.price:,.2f}")
    

    输出:

    New price for 'The Hunger Games' book is 7.66
    New price for 'Harry Potter and the Sorcerer's Stone' book is 5.26
    

    【讨论】:

    • 我希望我能给予更多的支持——这保持了 namedtuple 的简单性,增加了类的可变性,并且不需要任何其他包,无论轻量级还是重度。
    【解决方案3】:

    这看起来像是 Python 数据分析库 pandas 的任务。做这种事情真的非常容易:

    In [6]: import pandas as pd
    In [7]: df = pd.DataFrame(BSI, columns=Book._fields)
    In [8]: df
    Out[8]: 
               author                                  title    genre  year  \
    0  Suzane Collins                       The Hunger Games  Fiction  2008   
    1    J.K. Rowling  Harry Potter and the Sorcerers Stone  Fantasy  1997   
    
       price  instock  
    0   6.96       20  
    1   4.78       12  
    
    In [9]: df['price'] *= 100
    In [10]: df
    Out[10]: 
               author                                  title    genre  year  \
    0  Suzane Collins                       The Hunger Games  Fiction  2008   
    1    J.K. Rowling  Harry Potter and the Sorcerer's Stone  Fantasy  1997   
    
       price  instock  
    0    696       20  
    1    478       12  
    

    现在不是比与namedtuples 一起工作要好得多吗?

    【讨论】:

    • 有没有办法使用 _replace 方法达到同样的效果?
    • pandas 是相当重量级的 - 当需要一些更严重的数据操作时我会使用它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-21
    • 1970-01-01
    • 2017-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    相关资源
    最近更新 更多