为什么“导入”以这种方式实现?

时间:2013-09-18 03:46:06

标签: python import module

>>> import math
>>> math.pi
3.141592653589793
>>> math.pi = 3
>>> math.pi
3
>>> import math
>>> math.pi
3

最初的问题:为什么我不能让math.pi回来?

我认为import会将所有已定义的变量和函数导入当前范围。如果变量名已经存在于当前范围内,那么它将替换它。

是的,它确实取代了它:

>>> pi = 3
>>> from math import *
>>> pi
3.141592653589793

然后我想也许math.pi = 3分配实际上改变了math class(或者是math module?)中的属性,import math导入了该属性。

我是对的:

>>> import math
>>> math.pi
3.141592653589793
>>> math.pi = 3
>>> from math import *
>>> pi
3

所以,似乎:
如果你执行import x,那么它会导出x类似于类的东西。如果您对x.property进行了更改,则更改将保留在模块中,以便每次再次导入时,都是修改后的版本。

真实问题

  1. 为什么import以这种方式实施?为什么不让每个import math导入一个新的,未经修改的math副本?为什么要导入导入的math
  2. 在执行math.pi后,是否有任何解决方法可以让math.pi = 3回来(当然除了math.pi = 3.141592653589793)?
  3. 最初我认为import mathfrom math import *更受欢迎。但是这种行为让我担心如果我这样做,其他人可能会修改我导入的模块......我应该怎样做import

3 个答案:

答案 0 :(得分:13)

Python只创建任何给定模块的一个副本。重复导入模块会重复使用原始模块。这是因为如果模块A和B导入C和D,导入E和F等,C和D将被加载两次,E和F将被加载4次,等等。除了最琐碎的依赖之外图表,你会花费几分钟加载冗余模块,然后内存不足。另外,如果A导入的B和B导入了A,那么你会陷入一个递归循环,而且内存不足而没有做任何有用的事情。

解决方案:不要使用其他模块的内容。如果这样做,那就是解释器范围内的更改。有时候你会想要这样做,所以Python会让你,但这通常是一个坏主意。

答案 1 :(得分:10)

可以多次导入模块。 import语句只会从sys.modules加载引用。如果import语句也从磁盘重新加载模块,那么它将非常慢。修改这样的模块是非常不寻常的,只能在罕见的记录情况下完成,因此无需担心。

如何reload模块:

>>> import imp
>>> imp.reload(math)
<module 'math' (built-in)>
>>> math.pi
3.141592653589793

答案 2 :(得分:6)

导入行为旨在允许模块具有状态。例如,运行初始化代码的模块可能具有基于初始化时发生的各种不同行为(一个很好的例子是os模块,它根据你所使用的操作系统透明地加载不同版本的路径子模块) 。通常的行为是允许许多不同的代码访问模块而不重复运行初始化。此外,模块的功能类似于其他语言中的静态类 - 它们可以维护状态,通常用作全局变量的替代:例如,您可以使用语言环境模块来设置本地文化变量(货币格式等) - 调用代码的一部分中的 locale.setlocale 和另一部分中的 local.getlocale 是创建全局变量的一个很好的替代方法。

当然,你的例子指出了这个弱点。其中一个经典的python原则是

  

我们都是成年人

该语言并未提供您在Java或C#中找到的大部分隐私管理功能,这些功能可让作者锁定模块或类的内容。如果您感觉恶意(或只是自杀),您可以完成您的示例中完成的那种事情:将pi更改为等于3,或将函数转换为变量或各种其他令人讨厌的东西。这种语言并不是为了使这种语言难以实现 - 编码员需要负责。

@Josh Lee的回答显示了如何使用重新加载,这是将模块刷新到基于磁盘的状态的正确方法。使用重新加载的智慧主要取决于模块中的初始化代码量,以及导入或由相关模块导入的其他模块的Web。