我很好奇文件在从模块中需要时实际加载到内存中的频率。这是场景:
lookup.py
import json
mapping_dict = json.load(open('map.json'))
def map_file(file_name):
return(mapping_dict[file_name])
def other_function():
--do something--
main.py
import lookup
--something something--
for f in files:
print(lookup.map_file(f))
所以我的问题是来自lookup.py的mapping_dict加载了多少次?一旦初始导入?每次调用map_file?每次调用lookup.py中的任何函数时(在这种情况下为map_file和other_function)。
上面的一些组合?
答案 0 :(得分:0)
每个模块只加载一次。 import语句首先检查模块是否存在于sys.modules
中,如果存在,它只是执行字典查找。如果找不到该项,则会加载该模块。您可以使用reload
(仅限Python-2)或删除sys.modules
中的条目来强制重新导入模块(这有点像黑客攻击)。
In [1]: import sys as s1
In [2]: s1.modules['sys']
Out[2]: <module 'sys' (built-in)>
In [3]: del s1.modules['sys']
In [4]: id(s1) # different than s2 and s3
Out[4]: 140359411362736
In [5]: import sys as s2 # same as s3
In [6]: id(s2)
Out[6]: 140359175854240
In [7]: s1 == s2
Out[7]: False
In [8]: s2
Out[8]: <module 'sys' (built-in)>
In [9]: import sys as s3
In [10]: id(s3)
Out[10]: 140359175854240
请注意,重新加载给定模块不会重新加载它导入的任何库(仅在下面的Python2中,在Python3中,您可以使用importlib.reload):
>>> import sysconfig
>>> s = reload(sysconfig)
>>> id(sysconfig) == id(s)
False
>>> id(sysconfig.sys) == id(s.sys)
True
答案 1 :(得分:0)
正如评论中所回答的那样,但我会稍微展开一下,以便真正说明原因。
问题是,我们加载JSON文件的次数是多少次(来自if __name__ == 'main'
的第3行)
导入mapping_dict
时,假设周围没有{{1}}防护,{{1}}(对象)设置为json文件的内容。同样,定义的函数每个都分配给一个函数对象。您可以通过键入最后没有括号的函数名来测试它。您应该看到与对象
从那里开始,除非重新导入lookup.py,否则不会重新分配或更新mapping_dict。引用这些函数会引用它们所分配的对象。
答案 2 :(得分:0)
我不确定其他答案是否非常明确,所以让我发一些可能(或可能没有)帮助的其他内容。以下是在正常情况下导入模块时有效发生的事情:
sys.modules
(基本上是dict
)中查找具有要导入的模块名称的条目。如果它找到了某个东西,它将获取该对象,创建一个与模块名称对应的变量,并将该对象分配给变量。sys.modules
中找不到对象,则会创建一个对象来表示该模块,并将其放在sys.modules
下的相应名称下。然后它查找包含模块源代码的文件,将该文件作为Python脚本(ish)运行,然后挖掘之后存在的所有变量定义,并将它们指定为新模块对象的属性。请注意,这意味着文件中的代码仅在首次导入模块时执行一次。
在您的示例中,让我们看看它在实践中是如何工作的。 Python很高兴在main.py
中运行代码并遇到
import lookup
“好的(Python说),让我们转到sys.modules
并查看是否有任何内容存储在名称lookup
下。[...检查...]不,没有任何内容存储在姓名。查找lookup.py
的时间(使用sys.path
)。“
Python创建一个新的空模块对象,并将其放在名为sys.modules
的{{1}}下。然后它打开文件lookup
并开始运行代码。
lookup.py
Python现在以import json
名称sys.modules
查找。它将以递归的方式完成整个过程,但我将跳过详细信息,然后跳到json
下sys.modules
下的json
下完全加载的模块对象的位置。然后,Python将该对象分配给当前范围中的名称json
(实际上,它json = sys.modules['json']
}。
mapping_dict = json.load(open('map.json'))
Python现在加载JSON文件。我正在跳过大部分细节,但它涉及访问当前作用域中的json
变量(记住,指的是模块对象)并获取该对象的load
属性,并且然后叫它。来自该调用的内容将被放入当前范围中的另一个变量mapping_dict
。
def map_file(file_name):
return(mapping_dict[file_name])
Python现在看到def
并暂时停止执行;相反,它读取一段代码,将其编译为字节码,并将该字节码粘贴到一个函数对象中。该函数对象被分配给当前作用域中的变量map_file
。然后,回到执行。
def other_function():
--do something--
再一次,Python停止执行并读取一段代码,将其编译成字节码,并将该字节码粘贴到一个函数对象中,该函数对象被赋值给变量other_function
。
现在我们位于文件lookup.py
的末尾。我们在当前范围内有四个变量:json
,mapping_dict
,map_file
和other_function
。 Python将这四个变量指定为模块对象的属性。请记住,模块对象存储在密钥sys.modules
下的lookup
中,因此现在sys.modules['lookup']
是一个包含四个属性json
,mapping_dict
,{{ 1}}和map_file
。
回到other_function
的时间,我们仍在完成main.py
声明。该语句的最后一件事是将import lookup
的值赋给当前作用域sys.modules['lookup']
中的变量。 (实际上,lookup
。)同样,该对象有四个属性,如前所述。
继续
lookup = sys.modules['lookup']
在每次迭代循环中,Python访问for f in files:
print(lookup.map_file(f))
变量,该变量引用模块对象,然后访问该对象的lookup
属性,该属性基本上只包含一个一堆字节码。 Python然后调用函数对象,它实际上执行字节码 - 记住,这是map_file
定义中的代码,def map_file(...)
定义时{em>不执行}模块已加载。直到现在它才被执行。
所有代码都是访问lookup
并返回它。 mapping_dict[file_name]
只是一个引用现有对象的变量;大概是mapping_dict
。因此,访问dict
只需检查mapping_dict[file_name]
,看看它是否有一个带有与dict
值对应的键的条目,如果是,则返回它。访问此条目不会打开任何文件或执行任何复杂的操作。特别是,它不运行file_name
,因为该位代码不是函数的一部分。这段代码很久以前就被运行了,导入了json.load(open('map.json'))
,并且没有保存以便再次运行,所以在正常情况下它不会再运行。