Skip to main content
 首页 » 编程设计

python之有没有办法使用 Python RQ 从 __main__ 提交函数

2025年05月04日22telwanggs

与this question 类似, 有没有办法将同一文件中定义的函数提交给python-rq? @GG_Python 让我为此创建一个新问题。

使用示例:

# somemodule.py 
from redis import Redis 
from rq import Queue 
 
def somefunc(): 
    do_something() 
 
q = Queue(connection=Redis('redis://redis')) 
q.enqueue(somefunc) 

是的,我知道答案是在 someothermodule.py 中定义 somefunc,然后在上面的代码片段中 from someothermodule import somefunc,但我真的不想这样做。也许我对形式太执着了,但 somefunc 确实属于它排队的同一个文件(实际上,somefunc 采用 docker 容器名称并生成它)。我真的很希望整个事情都是独立的,而不是有两个模块。

我注意到,通过 python-rq 源代码挖掘,Queue.enqueue 实际上可以接受一个字符串而不是实际的模块,所以我希望我可以传递 somemodule.somefunc,但没有真幸运。有什么想法吗?

请您参考如下方法:

查看the source , rq 只是检查你的函数的 __module__ 属性,它可以被微不足道地改变。问题是,为什么 rq 限制您从 __main__ 入队作业?一定有一些原因,那就是:函数的模块必须可以被 worker 导入。 __main__ 不是,因为您的主模块未在磁盘上命名为 __main__.py。请参阅 this page 底部的“工作注意事项” .

此外,您的脚本中包含顶级(非定义)代码,每次由工作人员导入时都会重新调用,您可能不想这样做,因为它会创建新的队列和当每个 worker 开始时,无限地为他们提供工作。如果你想在主模块中加入一个函数,你可以而且应该使用 if __name__ == "__main__" 守卫来防止这种递归行为。

如果您想将函数及其排队保留在单个模块中,我的建议是除了函数和/或类定义之外,不要在其中放置任何顶级代码。任何可能是顶级代码的东西,都写成函数(例如命名为 main())。然后编写一个“虚拟”主模块,导入您的真实主模块并开始处理。

例子:

一些模块.py
from redis import Redis 
from rq import Queue 
 
def somefunc(): 
    do_something() 
 
def main(): 
    q = Queue(connection=Redis('redis://redis')) 
    q.enqueue(somefunc) 
 
# if the user tried to execute this module, import the right one for them. 
# somemodule is then imported twice, once as __main__ and once as somemodule, 
# which will use a little extra memory and time but is mostly harmless 
if __name__ == "__main__": 
    import mainmodule 
主模块.py
import somemodule 
somemodule.main() 

您也可以将函数的 __module__ 属性更改为磁盘上模块的实际名称,以便可以导入它。您甚至可以编写一个装饰器来自动执行此操作:

from sys import modules 
from from os.path import basename, splitext 
 
def enqueueable(func): 
    if func.__module__ == "__main__": 
        func.__module__, _ = splitext(basename(modules["__main__"].__file__)) 
    return func 
 
@enqueueable 
def somefunc(): 
    do_something() 
 
if __name__ == "__main__": 
    from redis import Redis 
    from rq import Queue 
 
    q = Queue(connection=Redis('redis://redis')) 
    q.enqueue(somefunc) 

为简单起见,装饰器假定您的模块是一个可在其文件名下导入的单个文件,并删除了 .py 扩展名。您可能正在为您的主模块使用一个包,其中事情会变得更加复杂...所以不要那样做。