关于什么东西是 Elisp 可以实现但是 Python 不能实现的。

不是争论,只是探讨,和编辑器无关。

我理解像这个 spy 这样的东西在 Python 里面是无法实现。从编程者角度讲还是有意义的。

1 个赞
def spy(x):
  print(f"{x} => {eval(x)}")

spy("1 + 2")
# 1 + 2 => 3

這樣?

这个我觉得还是有些区别的。

def spy(x):
    print(f"{x} => {eval(x)}")

def foo():
    x = 1
    spy("1 + x")

foo()
import inspect

def spy(fn):
    def spyprint(*args):
        print(inspect.getsource(fn) + "->")
        print(fn(*args))
    return spyprint

@spy
def useful(a,b):
    return a*b
   
useful(2,5)

也是可以的,實際上 Python 是可以這樣的

c = compile("1 + x", filename="", mode="eval")
x = 1
eval(c)  # 2

这里实际上 2 和 5 这两个参数的信息都丢失了。

要怎么把这个和之前的 spy 组合起来呢?

2 和 5 是啥 哦明白了我改改

这个 spy 的关键是在于把参数的表达式给打印出来了。

x = 2
y = spy(x + 5) 
# x + 5 => 7

需要的效果是这样的。

上面那个截图整理下打印格式你觉得满足要求么

不,不是这样的。你看换成 useful(x, 5) 时打印的应该是 x, 5

那没办法,你要的是打印编译期间的东西,py 只能包在 lambda 里面阻止它执行才能达到类似效果。

你是强行要求 spy 必须是个看起来和函数差不多的东西,但是 py 语法没有像 lisp 那么统一,它写不成函数那个样子,就只能写 @,@ 就可以拿原函数的 metadata 然后做处理,功能其实和宏是有重叠的。

decorator 也只是高阶函数,我理解的 Python 里面应该是写不出来类宏的东西。也许有人知道怎么做才可以,比较好奇。

還是可以的,但是你得手動用 ast 包調整,把整個 parse 進去後,改 ast 就行。

我没有用过 ast 包,但是似乎

  1. 有运行时的损耗
  2. 要用字符串来表达代码

宏能实现的功能在别的语言里面都是拆改成很多分散的替代品。比如你这个通过宏获取函数的源码和参数信息,那别的语言就用反射实现。

反射对应的是运行时,但是运行时的信息是已经有折损的了,一个叫 foo 的函数在运行时中未必还留有 foo 这个名字。

運行時損耗沒辦法,但可以不用字符串

你可以用 inspect.getsource 來得到源代碼,然後 ast.parse 得到 ast,然後修改再 eval

所以宏和反射只是功能有重叠。就比如 Java,反射很强但是写一些类似 lombok 的功能还是得手撸 AST。