通过脚本级命令行参数激活 Python 的优化模式
Activating Python's optimization mode via script-level command-line argument
我有一个脚本可以加载各种模块来完成它的工作。其中一些模块 大量 布满了 assert 语句——足以潜在地导致实际使用中的显着减速。 (我正在做很多涉及变异图的操作,所以有很多断言遍历整个图以强制不变量或检查结果的正确性。)所以我想允许用户禁用这些检查如果他们希望。看起来很简单;这正是 -O 标志的作用,对吧?
但是,问题在于 -O 是 Python 解释器处理的标志。我不想强迫我的用户显式调用解释器来访问此功能 - 我想支持 MyScript.py -O
,而不是 python -O MyScript.py
。有什么办法可以做到这一点吗?
我知道__debug__
不能设置在运行时;它作为不可变常量被特例化到语言中。但是,none 有问题的断言语句在主脚本中,我可以很容易地延迟加载我的其他模块,直到我完成对命令行参数的解析。因此,如果有某种方法可以从 Python 中设置 -O 标志或某种带有标志的导入功能,那就可以了。
注1:要明确的是,仅仅禁用断言语句是不够的;还有一些代码以 __debug__
常量(例如 if (__debug__): for x in large_expensive_set: assert some_property_of(x)
)为条件,不需要执行。
注 2:另一方面,我不关心代码内存大小,只关心速度,我的速度问题基于断言语句中的昂贵函数和仅调试代码中的昂贵循环。我知道 Python 的代码生成器,当 运行ning 在 -O 模式下时,会从编译代码中省略断言语句和 if (__debug__):
块,但如果不发生这种情况也没关系 -我不介意仅调试语句 存在 ,只要它们不执行即可。
注意 3:我考虑过只传递一个手动调试标志并用 if (self.__debug and not assert_condition): raise AssertionError()
替换所有断言。但是,该解决方案:
- 可读性极差
- 效率低得多
- 需要更改大量代码
- 需要重要的重构(每个对象都必须有一个调试标志,存储它,并将它传递给它生成的每个子对象)
如果设置了 __debug__
,您可以简单地重新启动自己。在脚本的顶部,试试这个:
if __debug__:
import os
import sys
os.execv(sys.executable, ['python', '-O'] + sys.argv)
这用使用 python -O
的新进程替换了 运行ning 进程。请注意,os.execv
永远不会 returns——它确实取代了 运行ning 过程。
这种方法的一个怪癖是它不会保留 Python 解释器的选项,因此如果您 运行 python -m cProfile myscript
它会在没有 cProfile
的情况下重新启动。我不确定如何解决这个问题...sys.flags
tells you which boolean options were set, but it doesn't tell you about -m
arguments. I thought this was interesting by itself, so created a separate question about it here:
我有一个脚本可以加载各种模块来完成它的工作。其中一些模块 大量 布满了 assert 语句——足以潜在地导致实际使用中的显着减速。 (我正在做很多涉及变异图的操作,所以有很多断言遍历整个图以强制不变量或检查结果的正确性。)所以我想允许用户禁用这些检查如果他们希望。看起来很简单;这正是 -O 标志的作用,对吧?
但是,问题在于 -O 是 Python 解释器处理的标志。我不想强迫我的用户显式调用解释器来访问此功能 - 我想支持 MyScript.py -O
,而不是 python -O MyScript.py
。有什么办法可以做到这一点吗?
我知道__debug__
不能设置在运行时;它作为不可变常量被特例化到语言中。但是,none 有问题的断言语句在主脚本中,我可以很容易地延迟加载我的其他模块,直到我完成对命令行参数的解析。因此,如果有某种方法可以从 Python 中设置 -O 标志或某种带有标志的导入功能,那就可以了。
注1:要明确的是,仅仅禁用断言语句是不够的;还有一些代码以 __debug__
常量(例如 if (__debug__): for x in large_expensive_set: assert some_property_of(x)
)为条件,不需要执行。
注 2:另一方面,我不关心代码内存大小,只关心速度,我的速度问题基于断言语句中的昂贵函数和仅调试代码中的昂贵循环。我知道 Python 的代码生成器,当 运行ning 在 -O 模式下时,会从编译代码中省略断言语句和 if (__debug__):
块,但如果不发生这种情况也没关系 -我不介意仅调试语句 存在 ,只要它们不执行即可。
注意 3:我考虑过只传递一个手动调试标志并用 if (self.__debug and not assert_condition): raise AssertionError()
替换所有断言。但是,该解决方案:
- 可读性极差
- 效率低得多
- 需要更改大量代码
- 需要重要的重构(每个对象都必须有一个调试标志,存储它,并将它传递给它生成的每个子对象)
如果设置了 __debug__
,您可以简单地重新启动自己。在脚本的顶部,试试这个:
if __debug__:
import os
import sys
os.execv(sys.executable, ['python', '-O'] + sys.argv)
这用使用 python -O
的新进程替换了 运行ning 进程。请注意,os.execv
永远不会 returns——它确实取代了 运行ning 过程。
这种方法的一个怪癖是它不会保留 Python 解释器的选项,因此如果您 运行 python -m cProfile myscript
它会在没有 cProfile
的情况下重新启动。我不确定如何解决这个问题...sys.flags
tells you which boolean options were set, but it doesn't tell you about -m
arguments. I thought this was interesting by itself, so created a separate question about it here: