我有一个python3
项目,具有以下目录结构:
project/
run.py
package/
a.py
b.py
constants.py
模块a
和b
使用各种公共变量/超参数。我需要在群集上运行具有不同超参数的项目的多个实例。我将作业提交给集群,然后集群安排它们。
我尝试过以下方法:
1.我在constants.py
里面有一个package
,我在提交每份工作之前对其进行了修改。假设我想运行5组不同的超参数。这种方法的问题是群集需要一段时间来安排我的工作,当它最终完成时,所有工作将使用存储在constants.py
中的最后修改的参数(即第5次运行)而不是5个不同的集合我想了。
2.接下来,我在argparse
中使用run.py
,但是无法通过a
和b
中的this SO thread和argparse
传递参数。
因此,我不得不求助的黑客是在run.py
中使用run.py
,在a
中导入'常数',重新初始化它们,然后在b
和sh
中的任何地方导入常量。这样,我可以为run.py
编写多个带有各种不同命令行参数的MCVE脚本,并在集群上安排它们。
我确信应该有一种更好(更pythonic)的方法来做到这一点。建议?谢谢。
因为我不太关注这里至少是project/
__init__.py
run.py
package/
__init__.py
a.py
b.py
constants.py
的开始。项目目录结构:
package
从from .a import ModelA
from .b import ModelB
目录(内部目录)开始,我有:
__init__.朋友
from . import constants
class ModelA:
def __init__(self, a, b):
print("Instantiating Model A with {0} {1}".format(a, b))
print(" Pi:{0} hbar{1}".format(constants.pi, constants.hbar))
啊.朋友
from . import constants
class ModelB:
def __init__(self, a, b):
print("Instantiating Model B with {0} {1}".format(a, b))
print(" Pi:{0} hbar{1}".format(constants.hbar, constants.pi))
不.朋友
hbar = 1
pi = 3.14
constants.朋友
project
请注意,init的内容纯粹是为了方便将ModelA
作为包导入,并立即在其下提供ModelB
和__init__.py
名称。我可以轻松地留下一个空的from project.package.a import ModelA
文件然后project
。
就像在project
dir中完成它一样。顶部目录(#!/usr/bin/evn python
import argparse
import package
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--param1', dest='param1')
parser.add_argument('--param2', dest='param2')
args = parser.parse_args()
package.ModelA(args.param1, args.param2)
package.ModelB(args.param2, args.param1)
)有一个空的__init__.py文件和一个:
润.朋友
$:> python3 project/run.py --param1 10 --param2 100
Instantiating Model A with 10 100
Pi:3.14 hbar1
Instantiating Model B with 100 10
Pi:1 hbar3.14
请注意,在环境管理可以发挥作用的群集上运行时,shebang链接可能不是必需的并且与情况有关。
从终端运行这个应该得到你
run.py
现在拿这个并改进你所拥有的东西或者用这些简化的术语重建你想要做的事情(希望打破这个例子)然后回发哪个部分不起作用以及为什么。
让我在解决方案前面加上一个声明,即做这样的事情就是让自己陷入失败之中。它看起来就像你有一个run.py
文件,你要在其中解析通过终端发送的参数,并使用它们来设置程序执行的全局状态。你不应该这样做,几乎永远(我知道的唯一例外是有时候,有时只有在连接到数据库时为引擎或会话设置全局状态时使用但是通常不是唯一或最好的解决方案)。
这就是您需要模块和包的原因。应该没有理由不能在a
中解析你的输入并且**调用任何子模块中的功能。 b
和from . import constants
from project.run import args
print("from A", args)
class ModelA:
def __init__(self, a, b):
print("Instantiating Model A with {0} {1}".format(a, b))
print(" Pi:{0} hbar{1}".format(constants.pi, constants.hbar))
中的函数有多少参数,或者它们是否采用和使用全部或不使用所发送的参数字面上没有区别。您可以编辑上面的示例,以便A类和B类只需要1或3或10,或A 5和B无参数,它仍然可以工作。
啊.朋友
from . import constants
from project.run import args
print("from B", args)
class ModelB:
def __init__(self, a, b):
print("Instantiating Model B with {0} {1}".format(a, b))
print(" Pi:{0} hbar{1}".format(constants.hbar, constants.pi))
不.朋友
#!/usr/bin/evn python
import argparse
from . import package
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--param1', dest='param1')
parser.add_argument('--param2', dest='param2')
args = parser.parse_args()
if __name__ == "__main__":
package.ModelA(args.param1, args.param2)
package.ModelB(args.param2, args.param1)
润.朋友
$:> python3 -m project.run --param1 10 --param2 100
from A Namespace(param1='10', param2='100')
from B Namespace(param1='10', param2='100')
Instantiating Model A with 10 100
Pi:3.14 hbar1
Instantiating Model B with 100 10
Pi:1 hbar3.14
然后调用它
args
请注意,没有太大变化。几乎任何事情,我们通过使用绝对导入路径而不是相对路径从run.py
导入if __name__ == "__main__":
然后我们将执行代码移动到python3 -m project.run
,以便在每次导入时都不会调用它 - 只有使该脚本成为“主”程序的那个。唯一更大和重要的区别是调用脚本。终端命令python3 project/run.py
将导入模块,然后将其作为脚本运行,而之前使用的run.py
将尝试运行__package__
作为脚本。首次导入模块时,会设置__package__
值;当设置from project.run import ...
时,它启用显式相对导入,因此run.py
语句有效,因为现在python知道它必须去哪里寻找这些值。当import package
像脚本一样运行时,当run.py
语句在旧的package/
中被调用时,Python进入run.py
目录但不知道它可能需要返回一级(到package/
)来搜索在然后将它们导入config.py
目录中的较低级别。
我建议使用可以为每个实例指定的环境变量
也许没有实际的常数,或者至少有一些import os
my_val=os.environ.get('MY_VAL', 'default value')
-e MY_VAL="some value"
然后,当您在多个实例上运行代码时,您需要在每次执行之间导出适当的变量。
如果你要将你的应用程序容器化,那么使用Docker,你就可以传入qazxswpoi并将其加载到代码中