Изменение значений списка namedtuples
У меня есть список namedtuples с именем 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 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'
Я понимаю, что я не могу установить поля в namedtuple. Как мне обновить 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
Кто-нибудь может сообщить мне, почему это происходит?
Ответы
Ответ 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 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 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)
Ответ 2
Это выглядит как задача для библиотеки анализа данных 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 Stone Fantasy 1997
price instock
0 696 20
1 478 12
Теперь не так много, намного лучше, чем работать с namedtuple
s?
Ответ 3
В Python> = 3.7 вы можете использовать декоратор класса данных с новой функцией аннотаций переменных для создания изменяемых типов записей:
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 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 Stone' book is 5.26