gpt4 book ai didi

Python:替换嵌套命名元组上的属性的简单方法?

转载 作者:行者123 更新时间:2023-12-01 05:23:19 25 4
gpt4 key购买 nike

我正在创建一个带有嵌套命名元组的数据结构(练习我的不可变函数式编程技能),但正在努力寻找一种简单的方法来替换嵌套命名元组中的值。

假设我有一个这样的数据结构:

from collections import namedtuple

Root = namedtuple("Root", "inventory history")
Inventory = namedtuple("Inventory", "item1 item2")
Item = namedtuple("Item", "name num")
Event = namedtuple("Event", "action item num")
r = Root(
inventory=Inventory(
item1=Item(name="item1", num=1),
item2=Item(name="item2", num=2)
),
history=(
Event(action="buy", item="item1", num=1),
Event(action="buy", item="item2", num=2)
)
)

# Updating nested namedtuples is very clunky
num_bought = 4
r_prime = r._replace(
history = r.history + (Event(action="buy", item="item2", num=num_bought),),
inventory = r.inventory._replace(
item2 = r.inventory.item2._replace(
num = r.inventory.item2.num + num_bought
)
)
)

# Contrast with the ease of using a version of this based on mutable classes:
r.history += Event(action="buy", item="item2", num=num_bought),
r.inventory.item2.num += num_bought

正如您所看到的,更改库存中项目的值非常痛苦,这要归功于 a) 被迫单独更新该值嵌套在其下的所有层,以及 b) 无法访问像这样的运算符+=

如果我要更新的库存中的项目是动态的,那么由于对 getattr 的调用遍布各处,情况会变得更加丑陋。

有没有更简单的方法来处理这个问题?

最佳答案

我创建了一个函数来更干净地处理这个问题。它还可以作为 namedtuple._replace() 的通用替代品。

Gist here ,代码复制如下。

child 参数是一个字符串,这有点困惑,但我想不出解决方法,而且由于命名元组已经将其属性定义为字符串,所以这不是一个 super 关闭- 无论如何,基本方法。

(至于这个困境是否仅仅因为 Python 不擅长处理不可变数据(因为 Python 没有针对函数式编程进行优化)而存在,请注意 this StackOverflow answer 表明 Haskell 遇到了非常相似的问题,并且 Haskell 库建议得到的解决方案在本质上类似于我的 Python 解决方案。)

我会稍等一下将此标记为答案,让互联网有机会提供更优雅的东西。

def attr_update(obj, child=None, _call=True, **kwargs):
'''Updates attributes on nested namedtuples.
Accepts a namedtuple object, a string denoting the nested namedtuple to update,
and keyword parameters for the new values to assign to its attributes.

You may set _call=False if you wish to assign a callable to a target attribute.

Example: to replace obj.x.y.z, do attr_update(obj, "x.y", z=new_value).
Example: attr_update(obj, "x.y.z", prop1=lambda prop1: prop1*2, prop2='new prop2')
Example: attr_update(obj, "x.y", lambda z: z._replace(prop1=prop1*2, prop2='new prop2'))
Example: attr_update(obj, alpha=lambda alpha: alpha*2, beta='new beta')
'''
def call_val(old, new):
if _call and callable(new):
new_value = new(old)
else:
new_value = new
return new_value

def replace_(to_replace, parts):
parent = reduce(getattr, parts, obj)
new_values = {k: call_val(getattr(parent, k), v) for k,v in to_replace.iteritems()}
new_parent = parent._replace(**new_values)
if len(parts) == 0:
return new_parent
else:
return {parts[-1]: new_parent}

if child in (None, ""):
parts = tuple()
else:
parts = child.split(".")
return reduce(
replace_,
(parts[:i] for i in xrange(len(parts), -1, -1)),
kwargs
)

关于Python:替换嵌套命名元组上的属性的简单方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21893843/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com