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

发布于 1周前 作者 bupafengyu 来自 Go语言

python 的 apscheduler 中 可以将 job 保存在数据库中,原理很简单 用的__import__.将字符串转换成可执行函数了。

但是 go 中没有啊。。。
我想写一个带持久化的 go 版的 apscheduler 。 这块卡住了。。。
Golang Go语言中是否能实现 Python 中 importlib 的功能

17 回复

我怀疑楼主不懂编译语言和解释执行语言的区别

更多关于Golang Go语言中是否能实现 Python 中 importlib 的功能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


走 cgo 绕一遍 dlopen?

可以在你的程序里内置一个脚本引擎,比如 lua

呃……你需要的是恢复“执行栈”?还是仅仅是持久化执行的结果?
不过 python 也不能恢复执行栈吧,所以我怀疑你只是想把当前的执行结果存档一下,可以用 gob (类似 pickle )

倒是要我设计能 resume status 的话……Go 没办法强制指定 pc 和符号表,要恢复“执行栈”,需要在所有函数出入口记录好参数状态(“表”驱动),函数版本,执行环境变量,通过 offset 指定恢复,应该可以(发现了什么奇怪的用途)

内置一个编译器,然后启动个子进程。

执行 go build 命令?

不可能的,还会有 goroutine 和 GC 的问题

----

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

想实现动态脚本?

用 build 代替你的__import__这种思路试试呗

都不是
上下文不需要保存。

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

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

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


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

谢谢大佬。

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

谢谢大佬。

go 的话,你已有的这个想法应该是唯一解了。。。顶多说写个 codegen 不用手动维护 map

可以 reflect.ValueOf(func).Call(params)这么整,但是还是要初始化

可以试试 yaegi ,github.com/traefik/yaegi ,感觉你说的是这个



我之前的想法是: 当你要定义一个新 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 动态性差,确实是不得不承认的

#12
reflect.ValueOf(func).Call(params)
这个 func 我没有办法通过字符串拿到。 还是只能通过 map 找了。

对,所以我说要初始化,但是应该可以用 codegen 来弄

在Go语言中,虽然没有直接等同于Python中importlib模块的内置功能,但Go通过其独特的包(package)机制和工具链,提供了灵活且强大的方式来动态加载和管理依赖。

Go的模块系统(自Go 1.11版本引入)允许你声明依赖关系,并通过go mod命令来管理这些依赖。虽然这不是在运行时动态导入模块,但它使得依赖管理更加明确和可靠。

对于需要在运行时动态加载代码的场景,Go社区通常推荐以下几种方式:

  1. 插件(Plugins):Go支持通过插件机制在运行时加载新的代码。插件必须编译为独立的.so(在Unix系统上)或.dll(在Windows上)文件,然后使用plugin包来加载和执行。

  2. 接口与依赖注入:通过定义接口并在运行时通过依赖注入的方式提供实现,可以实现一定程度的动态行为。这虽然不是动态加载代码,但可以在不修改代码的情况下改变行为。

  3. 代码生成:使用工具在编译时生成代码,从而模拟动态加载的效果。例如,可以使用模板或代码生成器来根据配置文件生成Go代码。

综上所述,虽然Go没有直接的importlib等价物,但通过上述机制,开发者可以实现类似的功能来满足特定的需求。

回到顶部