大量操作 dict 内元素时有什么能省略 dict 名字的语法糖?

2023-09-21 10:11:23 +08:00
 xuegy

标题可能描述的不够具体。大致操作是从 json 读入数据,经过一通计算,用 python-docx-replace 替换模版快速生成文档。代码举例(省略数字格式化字符串部分):

dict = json.load(xxx)
dict[A]=dict[B]+dict[C]
dict[D]=dict[E]-dict[F]
dict[G]=dict[H]*dict[I]
dict[J]=dict[K]/dict[L]
...
docx_replace(doc, **dict)

因为实际代码是很长的数学公式,个人觉得写这么多dict[]可读性实在太差,于是采用了如下写法(我知道不妥,但是想不出更好的),被 LD 批评很不 Pythonic 。

dict = json.load(xxx)
locals().update(dict)
A=B+C
D=E-F
G=H*I
J=K/L
...
dict.update({key: value for (key, value) in locals().items() if type(value) == int or type(value) == float})
docx_replace(doc, **dict)

求助各位高人有没有更合理的写法?

2638 次点击
所在节点    Python
34 条回复
aloxaf
2023-09-21 12:00:44 +08:00
我感觉只能用 locals 了,如果你们 leader 觉得不够 pythonic ,你可以间接使用,就像这样

def calc():
return A + B

env = {"A": 1, "B": 2}
print(eval(calc.__code__, env))
hitmanx
2023-09-21 12:07:51 +08:00
>> PL = PA * C3 * FR * k/(k-1.0) * N * C4 * (np.power(P0 / float(PA), (k-1.0) / (k*N)) - 1.0) / (EA * EM)

@xuegy 可能可以把表达式通过 Python AST( https://docs.python.org/3/library/ast.html)转成抽象语法树(AST),然后在 iterate 这个 AST 的时候把 node 替换成对应的 dict value
hitmanx
2023-09-21 12:11:09 +08:00
这是我让 chatgpt4 根据这个 idea 写的代码:
```
import ast
import json

data = json.load(xxx) # some sample data source


class TransformVarToDict(ast.NodeTransformer):
def visit_Name(self, node):
# Replace variable reference with dictionary access
if isinstance(node.ctx, (ast.Load, ast.Store)):
return ast.Subscript(
value=ast.Name(id='data', ctx=ast.Load()),
slice=ast.Index(value=ast.Str(s=node.id)),
ctx=node.ctx
)
return node


def process_formula(formula_str):
# Parse the formula to an AST
parsed = ast.parse(formula_str)

# Transform the AST
transformed = TransformVarToDict().visit(parsed)
ast.fix_missing_locations(transformed) # Fix line numbers

# Compile and execute the modified AST
code = compile(transformed, '<string>', 'exec')
exec(code, globals())


# Sample formulas
formulas = [
"A = B + C",
"D = E - F",
"G = H * I",
"J = K / L"
]


for formula in formulas:
process_formula(formula)


print(data)
```
xuegy
2023-09-21 12:12:08 +08:00
@hitmanx 不现实,我的任务是写一个类似于范文的东西,然后让一群学机械工程的人照着我这个范本写大量类似的东西出来。所以原则上来说,语法越像 MATLAB 那样简单直接越好。
hitmanx
2023-09-21 12:16:19 +08:00
@xuegy 你可能没明白这个意思。这个不需要你自己定义 DSL ,还是用 python 的 syntax 。

所以只要你的表达式本来就是 python 的 syntax ,直接一行不改应该就能用 python ast 把它 parse 成 AST 。然后只是遍历的时候把它替换成对应的 dict form 而已。

当然,所以依赖于 exec(string)的方法都会有 security 的问题,需要你的输入是 sanitized
MoYi123
2023-09-21 12:29:22 +08:00
json parse 到 class 里面, 然后加几个 method 去算不就好了? 用 ast 和 eval 真的有点搞了吧.
Maboroshii
2023-09-21 13:53:51 +08:00
加减乘除 括号,自己写一个解析字符串的计算器吧。
NoOneNoBody
2023-09-21 14:14:09 +08:00
@LandCruiser
@MoYi123
起初我也不明白,虽然繁琐,但无法避免逐条算式写,那先写简单的,然后在编辑器用正则替换就可以了,直到#24

对于不懂 python 的人来说,反而第二种可读性是高的
看 OP #24 所说,看样子恰好就是这样,要给不懂 python 的人写这堆算式,跟 dict 什么的无关,就是指代某个对象某个 item

最简便就是 SimpleNamaspace
In [18]: from types import SimpleNamespace

In [19]: d=SimpleNamespace(**{'A':1,'B':2,'C':3})

In [20]: d.A=d.B+d.C

In [21]: d
Out[21]: namespace(A=5, B=2, C=3)
victorc
2023-09-21 14:30:41 +08:00
“写这么多 dict[]可读性实在太差”--- 这样写反而清楚明了

写程序要朴素,别玩花了
ipwx
2023-09-21 14:36:51 +08:00
为什么我觉得楼主这个场景里面,需要执行的代码是可信的(自己写的),只不过变量值是外部读入的?

那直接构造 locals() 用 exec/eval 不就行了。。。
pursuer
2023-09-21 14:40:40 +08:00
直接修改 locals 真的可以吗? 我记得不行啊,只能改 globals 或者 exec ,我刚刚还特意去试了下 3.8 和 3.10 不行啊,是哪个版本支持了吗?
xuegy
2023-09-21 15:43:13 +08:00
@NoOneNoBody 这个和 easydict 的简化效果差不多。不过 easydict 更好一点,可以直接送进 python-docx-replace 替换
xuegy
2023-09-21 15:48:54 +08:00
@ipwx 原理上是这样的,主要是这个东西的可维护性和普通 python 代码的可维护性不是一回事。
我希望它是一种把前后都写好,中间就能像 MATLAB 一样的,这样机械工程的人可以直接往中间填公式。
ZX576
2023-09-21 18:44:28 +08:00
用 Pydantic 序列化,然后 __init__ 里修改,最后再 dict 导出,既清晰,扩展性又强,还能随便帮你检查 dict 中的值

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/975773

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX