6

我是 Python 新手,有一些事情困扰了我很长一段时间。我在 Mark Lutz 的“Learning Python”中读到,当我们使用from语句导入模块中存在的名称时,它首先导入模块,然后为其分配一个新名称(即函数、类等的名称)。存在于导入的模块中),然后使用该del语句删除模块对象。但是,如果我尝试使用from引用导入的模块中的名称来导入名称,而该名称本身并未导入,会发生什么情况?考虑以下示例,其中有两个模块mod1.pymod2.py

#mod1.py
from mod2 import test
test('mod1.py')        

#mod2.py
def countLines(name):
    print len(open(name).readlines())

def countChars(name):
    print len(open(name).read())

def test(name):
    print 'loading...'
    countLines(name)
    countChars(name)
    print '-'*10

现在看看当我运行或导入 mod1 时会发生什么:

>>>import mod1

loading...
3
44
----------

这里当我导入并运行test函数时,虽然我没有导入countCharsor countLines,但它运行成功,并且from语句已经删除了mod2模块对象。

所以我基本上需要知道为什么这段代码可以工作,即使考虑到我提到的问题它不应该。

编辑:非常感谢所有回答的人:)

4

6 回答 6

6

每个函数都有一个__globals__属性,该属性保存了它搜索全局变量和函数的环境的引用。

然后将该test函数链接到 的全局变量mod2。因此,当它调用countLines解释器时,即使您在导入函数的模块中编写了一个具有相同名称的新函数,解释器也总能找到正确的函数。

于 2012-08-14T18:13:12.643 回答
4

我认为您正在与 python 处理的方式搏斗namespaces。当您键入时,from module import thing您将thing带入module您当前的命名空间。因此,在您的示例中,当mod1被导入时,代码按以下顺序进行评估:

from mod2 import test #Import mod2, bring test function into current module namespace
test("mod1.py")  #run the test function (defined in mod2)

现在对于 mod2:

#create a new function named 'test' in the current (mod2) namespace 
#the first time this module is imported.  Note that this function has
#access to the entire namespace where it is defined (mod2).
def test(name):  
    print 'loading...'
    countLines(name)
    countChars(name)
    print '-'*10

所有这一切都很重要的原因是因为 python 允许您准确地选择要拉入命名空间的内容。例如,假设您有一个module1which defined function cool_func。现在您正在编写另一个模块 ( module2),并且它也使得 formodule2也具有一个功能cool_func。Python 允许您将它们分开。在module3你可以这样做:

import module1
import module2
module1.cool_func()
module2.cool_func()

或者,您可以这样做:

from module1 import cool_func
import module2 
cool_func() #module1
module2.cool_func()

或者你可以这样做:

from module1 import cool_func as cool
from module2 import cool_func as cooler
cool()  #module1
cooler() #module2

可能性继续...

希望我的观点很清楚。当你从一个模块中导入一个对象时,你是在选择你想如何在你当前的命名空间中引用那个对象。

于 2012-08-14T18:07:40.447 回答
3

其他答案比这个更好,但是如果您运行以下命令,您可以看到countChars并且countLines实际上都定义在test.__globals__

from pprint import pprint
from mod2 import test

pprint(test.__globals___)
test('mod1')

您可以看到导入test带来了 中定义的其他全局变量mod2,让您运行该函数而不必担心必须导入您需要的所有内容。

于 2012-08-14T18:12:53.697 回答
1

一条import语句将整个模块加载到内存中,这就是test()函数成功运行的原因。

但是当您使用from语句时,这就是为什么您不能直接使用countLinesandcountCharstest肯定可以调用它们的原因。

from语句基本上加载整个模块并将导入的函数、变量等设置为全局命名空间。

例如。

>>> from math import sin
>>> sin(90)               #now sin() is a global variable in the module and can be accesed directly
0.89399666360055785
>>> math
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    math
NameError: name 'math' is not defined
>>> vars()  #shows the current namespace, and there's sin() in it
{'__builtins__': <module '__builtin__' (built-in)>, '__file__': '/usr/bin/idle', '__package__': None, '__name__': '__main__', 'main': <function main at 0xb6ac702c>, 'sin': <built-in function sin>, '__doc__': None}

考虑一个简单的文件 file.py:

def f1():
   print 2+2

def f2():
   f1()

仅导入 f2:

>>> from file import f2
>>> f2()
4

虽然我只导入了,f2()f1()它运行f1()成功,这是因为模块已加载到内存中,但我们只能访问f2(),但f2()可以访问模块的其他部分。

于 2012-08-14T18:08:28.583 回答
1

Python 命名空间指南

尽管模块有自己的全局命名空间,但这并不意味着所有名称都可以在模块中的任何地方使用。范围是指程序的一个区域,可以从该区域访问命名空间而无需前缀。范围对于它们在模块中提供的隔离很重要。任何时候都有许多作用域在运行:你所在的当前函数的作用域、模块的作用域,然后是 Python 内置函数的作用域。这种范围的嵌套意味着一个函数不能访问另一个函数内的名称。

名称空间也会从里到外搜索名称。这意味着如果在模块的全局命名空间中声明了某个名称,您可以在函数内部重用该名称,同时确保任何其他函数都将获得全局名称。当然,您可以通过在名称前加上“global”关键字来强制函数使用全局名称。但是如果你需要使用它,那么你最好使用类和对象。

于 2012-08-14T18:11:35.627 回答
1

每个模块都有自己的范围。在 内mod1,您不能使用名称countLinescountChars(或mod2)。

mod2它本身不会受到它碰巧在其他地方进口的方式的影响;其中定义的所有名称都在模块中可用。

如果您引用的网页确实说该del语句删除了模块对象,那就错了。del只删除名称,不删除对象。

于 2012-08-14T18:07:37.700 回答