Go 语言是否能实现 Python 中 importlib 的功能

2022-04-19 20:55:45 +08:00
 chaleaochexist
python 的 apscheduler 中 可以将 job 保存在数据库中,原理很简单 用的`__import__`.将字符串转换成可执行函数了。

但是 go 中没有啊。。。
我想写一个带持久化的 go 版的 apscheduler 。 这块卡住了。。。
2150 次点击
所在节点    Go 编程语言
16 条回复
iBugOne
2022-04-19 20:56:56 +08:00
我怀疑楼主不懂编译语言和解释执行语言的区别
westoy
2022-04-19 20:59:56 +08:00
走 cgo 绕一遍 dlopen?
seakingii
2022-04-19 21:04:42 +08:00
可以在你的程序里内置一个脚本引擎,比如 lua
mengzhuo
2022-04-19 21:50:32 +08:00
呃……你需要的是恢复“执行栈”?还是仅仅是持久化执行的结果?
不过 python 也不能恢复执行栈吧,所以我怀疑你只是想把当前的执行结果存档一下,可以用 gob (类似 pickle )

倒是要我设计能 resume status 的话……Go 没办法强制指定 pc 和符号表,要恢复“执行栈”,需要在所有函数出入口记录好参数状态(“表”驱动),函数版本,执行环境变量,通过 offset 指定恢复,应该可以(发现了什么奇怪的用途)
gabon
2022-04-19 21:52:26 +08:00
内置一个编译器,然后启动个子进程。
iyaozhen
2022-04-19 23:39:23 +08:00
@gabon 执行 go build 命令?
GeruzoniAnsasu
2022-04-20 00:00:52 +08:00
@mengzhuo 不可能的,还会有 goroutine 和 GC 的问题

----

OP 换个思路:
在静态语言中,所有函数都是「已持久化」的、嵌在程序中的。借助反射,函数的地址可以通过函数名查到,所以执行体存个名字就好了
job 的另一个组成部分是参数,而保存参数还是比较简单的,毕竟有反射,如果你想,可以把 runtime 对象整个扫描一遍再用反射造回来
Vegetable
2022-04-20 00:28:37 +08:00
想实现动态脚本?

用 build 代替你的__import__这种思路试试呗
chaleaochexist
2022-04-20 00:28:39 +08:00
@mengzhuo 都不是
上下文不需要保存。

我从 redis 里面把函数名和函数参数读取出来然后执行。 大概是这个意思。

python 可以通过保存"module_name.func_name" 然后
```python
func = __import__("module_name.func_name")
func()
```
这样执行。

pickle 也可以,虽然我没试过 但是我觉得 gob 应该也可以。 但是把函数序列化,在反序列化, 应该没人这么干吧。


目前的想法是在 main 启动的时候 配一个 map 将函数名和函数映射起来。 至于参数, 可以用我楼上说的反射实现。

谢谢大佬。
chaleaochexist
2022-04-20 00:31:52 +08:00
@GeruzoniAnsasu #7 大佬说的反射是泛指还是特指 Go 的反射。 因为我没想明白 在 go 语言中如何通过函数名查找到函数的地址。 如果能实现的话, 那我的问题其实差不多就解决了。

谢谢大佬。
learningman
2022-04-20 00:54:42 +08:00
go 的话,你已有的这个想法应该是唯一解了。。。顶多说写个 codegen 不用手动维护 map
learningman
2022-04-20 00:55:59 +08:00
可以 reflect.ValueOf(func).Call(params)这么整,但是还是要初始化
275761919
2022-04-20 09:30:32 +08:00
可以试试 yaegi ,github.com/traefik/yaegi ,感觉你说的是这个
GeruzoniAnsasu
2022-04-20 09:53:55 +08:00
@chaleaochexist

我之前的想法是: 当你要定义一个新 Job 的时候: Schedule(Callback,time) Callback 必然已经是一个静态的函数了,而要持久化,callback 又不能是闭包。那么比如要求 Callback 写成固定名 struct 的 method ,类似这样:

https://go.dev/play/p/cUw99-T55v8


然后又想能不能根据类型信息反射出一个类实例,然后类实例包含一个重写掉的 Run method ,这样函数名就是固定的了,而且定义 Job 的方式能更灵活。 问题集中在怎么得到这个有类型的实例上——


然而研究了大概 6 个小时之后我发现
1. reflect.Type 也是一个接口,意味着就算把类型信息 dump 出来了,我也没法轻易构造出一个实例化的 Type
2. reflect.rtype 是 reflect.Type 接口最重要的实现,这个结构是有办法 dump 的( unsafe pointer 读),但不能存在覆写一个现存 reflect.rtype 的办法。 通过反射写这个结构会被 reflect.Value.SetXXX 的实现拒绝(有 flag 阻止写回去);而直接得到这个结构的 unsafepointer 尝试给它赋值会触发访问违例,原因不明。由于我也没用更 low level 的调试器去调( goland 而已),所以我也不知道是赋值语义还是类型转换语义的问题
3. 不像 C/++ 有函数地址就可以强制转换出一个函数; golang 是无论如何都先得有 reflect.Type 的(光有地址没有用)。由于 2 ,reflect.Type 不能自由构造,因此在语言范围内能想到的 tricky way 都堵死了。




有点蛋疼,前几天才有其他人说 golang 动态性差,确实是不得不承认的
chaleaochexist
2022-04-20 10:02:50 +08:00
@learningman #12
reflect.ValueOf(func).Call(params)
这个 func 我没有办法通过字符串拿到。 还是只能通过 map 找了。
learningman
2022-04-20 10:27:44 +08:00
@chaleaochexist 对,所以我说要初始化,但是应该可以用 codegen 来弄

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

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

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

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

© 2021 V2EX