让test
作为我们以__main__
运行的模块。该模块包含一个名为primes
的全局变量,该变量在模块中使用以下分配进行了初始化。
primes = []
该模块还包含一个名为pi
的函数,该函数会更改此全局变量:
def pi(n):
global primes
"""Some code that modifies the global 'primes' variable"""
然后我想使用内置的timeit
模块来计时所述功能。我想使用timeit.repeat
函数并获取计时的最小值,以提高测量精度(而不是只测量一次,因为不相关的过程可能会降低速度)。
print(min(timeit.repeat('test.pi(50000)',
setup="import test",
number=1, repeat=10)) * 1000)
问题在于pi
函数的行为取决于primes
的值:我希望对于每次重复,import test
参数中的setup
语句会在primes = []
中重新运行test
语句,从而“重置” primes
,以使每次重复执行的代码都相同。但是,取而代之的是,使用了前一次执行产生的primes
的值,因此我不得不在test.primes = []
参数中添加语句setup
:
print(min(timeit.repeat('test.pi(50000)',
setup="import test \n" + "test.primes = []",
number=1, repeat=10)) * 1000)
这引出我一个问题:是否有一种直接方法(即,在一条语句中)将 all 全局变量的值“重置”为在变量中首次分配时的值模块?
在这种特定情况下,添加一个语句以手动“重置” primes
可以很好地工作,但是考虑到存在很多全局变量,而您想“重置”所有这些变量的情况。 / p>
为什么语句import test
不重新运行初始的primes = []
分配?
答案 0 :(得分:1)
让我们从您的附带问题开始,因为事实证明,它实际上是一切的中心:
为什么语句
import test
不重新运行初始的primes = []
分配?”
因为,正如the import system和the import
statement上的文档中所述,import test
的作用是松散地使用了以下伪代码:
if 'test' not in sys.modules:
find, load (compiling if needed), and exec the module
sys.modules['test'] = result
test = sys['test.modules']
好,但是为什么这样做?
如果您有两个都导入相同模块的模块,则它们希望看到相同的全局变量。请记住,在函数顶层定义的类型,函数等都是全局变量。例如,如果sortedlist.py
将collections.abc
导入class SortedList(collections.abc.Sequence):
,而scraper.py
则将collections.abc
导入isinstance(something, collections.abc.Sequence)
,则您需要一个{{1} }以通过该测试-但是,如果它们是两个完全独立的类型,则不会,因为它们来自恰好具有相同名称的两个不同的模块对象
如果所有SortedList
都具有12个模块,则将运行所有Pandas初始化代码12次。除了您的某些模块可能还会相互导入之外,因此它们将分别运行多次,并每次都导入Pandas。您认为运行所有Pandas初始化60次需要多长时间?
因此,重复使用现有模块几乎总是您想要的。
如果您不这样做,通常表明您的设计存在问题(这里可能就是这种情况)。
但是“几乎总是”不是“总是”。因此,有很多解决方法。对于实时代码,通常都不是一个好主意,但是对于单元测试和基准测试而言,只要权衡取舍就可以使用三个基本选项:
del sys.modules['test']
。这显然是很棘手的,但实际上确实可以满足您在这里想要的目的。现有的对旧模块的引用完全没有变化,但是下一次有人import pandas as pd
时,他们将获得一个全新的import test
模块。importlib.reload(test)
。这听起来不错,但一方面可能会过大(请注意,它会强制重新编译模块源,而您不需要),而另一方面,这可能还不够(实际上并不会重置全局变量-如果您的代码在最高级别执行了test
,则该行将被执行,所以谁在乎,但是如果您的代码却在primes = []
函数中执行了globals().setdefault('primes', [])
,则您关心)。pi
,手动执行执行模块的所有步骤(请参阅import test
文档中的examples),但不要将其存储在{{1}中}或importlib
中,只需将其存储在每次测试后丢弃的局部变量中即可。这可能是最干净的,尽管它的确意味着6行代码而不是1行。