【问题标题】:returning a replaced field in a list of namedtuples in a namedtuple在命名元组中的命名元组列表中返回替换字段
【发布时间】:2015-10-07 18:28:38
【问题描述】:

我有一个如下定义的 Restaurant 和 Dish 类型 namedtuple:

Restaurant = namedtuple('Restaurant', 'name cuisine phone menu')
Dish = namedtuple('Dish', 'name price calories')

r1 = Restaurant('Thai Dishes', 'Thai', '334-4433', [Dish('Mee Krob', 12.50, 500),
                                                    Dish('Larb Gai', 11.00, 450)])

我需要将菜品的价格更改 2.50。我有以下代码:

def Restaurant_raise_prices(rest):
result = []
for item in rest:
    for dish in item.menu:
        dish = dish._replace(price = dish.price + 2.50)
        result.append(item)
return result

它替换价格字段并返回 Dish namedtuple:

[Dish(name='Mee Krob', price=15.0, calories=500), Dish(name='Larb Gai', price=13.5, calories=450)]

如何更改我的代码以添加餐厅?

但它只返回 Dish。如果我也想要整个餐厅怎么办?如何进行更改以使输出为:

Restaurant('Thai Dishes', 'Thai', '334-4433', [Dish(name='Mee Krob', price=15.0, calories=500), Dish(name='Larb Gai', price=13.5, calories=450)])

【问题讨论】:

  • 不要为此使用命名元组。此外,您的代码不会产生该输出……替换 dish不会更新餐厅内的(不可变)元组。

标签: list python-3.x iteration tuples


【解决方案1】:

命名元组首先是元组,因此不可变。这意味着您无法修改现有对象。如果您想更改它们,则必须创建包含新值的 元组,并将对旧元组的所有引用替换为该新元组。

您使用的代码对此不起作用,因为dish = dish._replace(…) 将用新元组替换dish 的值,但更改dish 引用的内容不会更新餐厅元组中存在的引用。此外,由于 result.append(item) 行是您迭代菜肴的内部循环的一部分,您最终会在结果中得到同一个餐厅元组的多个(未更改!)副本。

您可以像这样更改它以使其工作(顺便说一句。假设您只将一家餐厅传递给该函数 - 所以您只需要一个循环来处理菜肴):

def restaurant_raise_prices (rest):
    dishes = []
    for dish in rest.menu:
        dishes.append(dish._replace(price=dish.price + 2.50))
    rest = rest._replace(menu=dishes)
    return rest

这将返回一家新餐厅,其中每道菜的价格都已更改(请注意,r1 不会反映此更改):

>>> r1
Restaurant(name='Thai Dishes', cuisine='Thai', phone='334-4433', menu=[Dish(name='Mee Krob', price=12.5, calories=500), Dish(name='Larb Gai', price=11.0, calories=450)])
>>> restaurant_raise_prices(r1)
Restaurant(name='Thai Dishes', cuisine='Thai', phone='334-4433', menu=[Dish(name='Mee Krob', price=15.0, calories=500), Dish(name='Larb Gai', price=13.5, calories=450)])

然而,一种更简洁的方法是引入适当的可变类型,这样你就可以让它变得更好一些。毕竟,餐厅是可以改变的对象:他们可以随时修改菜单,而不会成为一家新餐厅。因此,让餐厅和菜肴成为可变对象是有意义的:

class Restaurant:
    def __init__ (self, name, cuisine, phone):
        self.name = name
        self.cuisine = cuisine
        self.phone = phone
        self.menu = []

    def __str__ (self):
        return '{} ({}) - {} ({} dishes)'.format(self.name, self.cuisine, self.phone, len(self.menu))

class Dish:
    def __init__ (self, name, price, calories):
        self.name = name
        self.price = price
        self.calories = calories

    def raise_price (self, amount):
        self.price += amount

    def __str__ (self):
        return '{} (price: {}, calories: {})'.format(self.name, self.price, self.calories)
>>> r1 = Restaurant('Thai Dishes', 'Thai', '334-4433')
>>> r1.menu.append(Dish('Mee Krob', 12.50, 500))
>>> r1.menu.append(Dish('Larb Gai', 11.00, 450))

>>> print(r1)
Thai Dishes (Thai) - 334-4433 (2 dishes)
>>> for dish in r1.menu:
        print(dish)

Mee Krob (price: 12.5, calories: 500)
Larb Gai (price: 11.0, calories: 450)

>>> for dish in r1.menu:
        dish.raise_price(2.50)
        print(dish)

Mee Krob (price: 15.0, calories: 500)
Larb Gai (price: 13.5, calories: 450)

【讨论】:

  • 感谢您的详细解释。这有很大帮助。有没有办法让函数不包含 menu= 部分?那就是输出与输入完全相同(加上价格变化)。 Restaurant('Thai Dishes', 'Thai', '334-4433', [Dish(name='Mee Krob', price=15.0, calories=500), Dish(name='Larb Gai', price=13.5, calories=450)])
  • namedtuple 的输出是固定的,并且将始终明确包含属性名称,您无法修改它。如果您使用自定义类型,则可以按照我显示的那样调整输出。
猜你喜欢
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-09
相关资源
最近更新 更多