突然想到 py 一个简单的函数字符串缩写语法糖,不知道有没有现成的?

2021-08-08 12:27:23 +08:00
 O5oz6z3
import magic

# 1 lambda 匿名函数表达式识别:没有分号`;`或者无括号参数声明 `args:`
magic | "_: _.attr+1"
magic | "*a,**k: a[0].attr+1"

# 2 def 多行函数识别:检测分号`;`或者括号参数声明`(args):`或者函数声明`def (args):` 
magic | "_.attr+= 1; yield _.attr"
magic | "(*a,**k): b=a[0].attr+1; yield b"
magic | "def(*a,**k): b=a[0].attr+1; yield b"
magic | "def *a,**k: b=a[0].attr+1; yield b"

# 3 传递位置参数引用:等价于 partial(func, *args),参数从左侧开始绑定。
magic | ["a,b: a+b", a, b]

# 4 传递关键字参数引用:等价于 partial(func, **kwrds)
magic("a,b,c=None: c or a+b", c=c)

# 5 同时传递位置参数和关键字参数引用:等价于 partial(func, *args, **kwrds)
magic("a,b,c=None: c or a+b", a, b, c=c)

# 6 隐式参数:`_`
magic | "_.attr+1"
magic | "def: b=_.attr+1; yield b"

突然想到这么一个简单的语法糖格式,不知道是否已经被人提出实现过了?还是说这种魔法语法糖没有实用价值?那么有没有类似的纯字符串 DSL 语法糖库?或者大家有什么其他看法?

原理是将多行函数或函数表达式的缩写写在字符串里,然后构造出函数。

实现思路是简单地检测字符串,也可以用正则,甚至手写解释器。 转换回完整 python 函数也简单,补充完整函数声明然后 eval/exec 。

缺点是传值、闭包引用会比较麻烦,虽然应该可以通过 inspect 实现,但这么复杂和不纯函数不建议用字符串缩写。如果是完整的函数代码字符串,那根本就不需要缩写了。

优点是导入即可使用。而且可以进一步拓展支持自定义的函数语法糖格式。比方说想要更好可读性的语法糖,只要实现了对应 DSL 字符串转化为 python 函数的解释器就可以了。

2721 次点击
所在节点    Python
18 条回复
hsfzxjy
2021-08-08 12:44:29 +08:00
我实现过一个。函数体不用写在字符串里,比你描述的更容易使用。https://github.com/hsfzxjy/lambdex
0ZXYDDu796nVCFxq
2021-08-08 14:16:09 +08:00
代码除了实现功能以外,还有很重要一点就是要给别人看的
lichdkimba
2021-08-08 17:30:53 +08:00
这看不懂啊。。。。。
efaun
2021-08-08 17:52:07 +08:00
@gstqc #2 python3.9 又加了几个语法糖,真的学不动了……
raaaaaar
2021-08-08 18:09:02 +08:00
语法糖太多的确看着难受,当然是因为我不太熟 py
O5oz6z3
2021-08-08 19:15:29 +08:00
@gstqc @lichdkimba @efaun @raaaaaar
不好意思,我写得太抽象了,补充了一些描述在附言里。

@raaaaaar #5
这不是你的问题,是我的问题,python 没有这些语法糖。只是我自己想到了一个很简单就可以实现的伪语法糖,想问问大家意见。
O5oz6z3
2021-08-08 19:28:26 +08:00
@hsfzxjy #1
确实大开眼界,语法很自由可读,设计很完善,完成度非常高,大受震撼……不过话说回来,我的目标是缩写 lambda,你的目标是强化 lambda,方向不一样。
hsfzxjy
2021-08-08 19:38:13 +08:00
@O5oz6z3 #7 抱歉,下午只看到了第二点。我知道有一些库能近似达到部分要求,如 https://github.com/dry-python/lambdas https://github.com/abersheeran/cool 。不过这类库都不会选择把函数体写在字符串里,那样可读性太差了,大家倾向于用现有语法组装成 DSL 。
LeeReamond
2021-08-08 19:48:14 +08:00
并不好用,并不好读,并不 magic
O5oz6z3
2021-08-08 20:06:17 +08:00
@hsfzxjy #8 谢谢!
说实话,这个帖子的想法正是来自于实现管道语法糖,整件事颇有点函数式尾递归的味道,其中遇到了需要经常定义 lambda 的问题,然后就想到了这个伪语法糖,想抛砖引玉一下。

先不说我水平有限无法完整实现 DSL,要我用现成的语法组成 DSL 就变成 ORM 了,所以偷懒直接沿用 python 自己的语法。

@LeeReamond #9 确实!所以想参考大家意见……
ztcaoll222
2021-08-08 20:45:34 +08:00
[格林斯潘第十定律] 。。。
ClericPy
2021-08-08 22:37:21 +08:00
需求描述的比抽象语法树还抽象... 一点都没感觉带来简便啊
KAAAsS
2021-08-08 23:14:38 +08:00
没高亮的话行内代码看着有点头疼……
而且我感觉其实也不是一定强求单行吧?适度的换行也是可以接受的,比如:
```haskell
func x = case x of
1 -> 2
2 -> 3
otherwise -> 4
```
O5oz6z3
2021-08-09 00:37:53 +08:00
@ztcaoll222 #11 不懂 lisp,所以不知道指的是哪一点像……

@ClericPy #12 比 AST 还抽象那是不敢当……不过我的确也说不明白,大概是三个问题的混合:
1. lambda 前缀写起来有点长,有没有什么库提供更简便的生成函数表达式的方法。
2. 有关 python 的 DSL 库,有没有什么值得推荐的库。
3. 针对第一点,想到一个“简单”的解决思路,这个思路如何、是不是已经有人实现过了。

不过我的“解决思路”也描述得很抽象,所以讨论也无从谈起……

@KAAAsS #13 你说的代码高亮确实是个问题。强求单行则是为了能在表达式里塞下函数定义,比方说 map() 需要的映射函数,有了匿名函数就可以直接在定义在表达式里`map((lambda a: a+2), list)`,如果用换行的方式就不太方便了。
1018ji
2021-08-09 18:03:22 +08:00
看不懂
O5oz6z3
2021-08-09 21:50:47 +08:00
@1018ji #15 是我说复杂了,简单来说的话就是:“把函数定义放在字符串里,定义的时候就可以省略关键字 lambda 、def 和函数名。这样的语法糖设计大家觉得实用吗?”(虽然这样的设计实现起来很简单,但是没有了语法高亮和可读性)
abersheeran
2021-08-13 15:18:19 +08:00
不如把整个文件作为一个 Python 超集进行解析,转成 Python ast 跑。
O5oz6z3
2021-08-14 06:54:09 +08:00
@abersheeran #17 超集的确会语法更灵活,但实现难度也高很多……

顺带一提找到了一个库 ideas 似乎能较简单地自定义语法糖,比如替换 lambda 关键字:github.com/aroberge/ideas

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

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

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

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

© 2021 V2EX