Python忍者秘籍
上QQ阅读APP看书,第一时间看更新

模块的一个关键点是它们产生单独的命名空间。命名空间(也称为范围)只是模块或组件的控制域。通常,模块内的对象在该模块外部不可见,也就是说,试图调用位于单独模块中的变量将产生错误。

命名空间也用于隔离同一程序中的对象。例如,函数内定义的变量只能在该函数运行时使用,试图从另一个函数调用该变量会导致错误。这就是为什么全局变量是可用的,它们可以被任何函数调用并相互作用。这也是全局变量不被看作最佳实践的原因,因为用户可能修改了全局变量而没有意识到这一点,从而在程序的后面部分造成中断。

命名空间基本上在内部起作用。如果在函数中调用变量,Python解释器将首先在该函数中查找变量的声明。如果它不在函数中,Python将向上移动堆栈并寻找全局定义的变量。如果还没有找到,Python将查看内置的库,这些库始终是可用的。如果仍未找到,Python将引发一个错误。在流程方面,它看起来像这样:局部范围→全局范围→内置模块→错误。

当导入模块时,对范围发现过程产生的一个微小变化,导入的模块也会检查对象调用。但需要注意的是,除非通过点命名法显式标识期望的对象,否则仍然会生成错误。

例如,如果希望生成0~1000的随机数,则不能只调用randint()函数而不导入random库。一旦导入了模块,公共可用的类、方法、函数和变量就可以通过使用<module_name>和<object_name>明确地调用它们。以下是一个例子。

>>> randint(0, 1000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'randint' is not defined
>>> import random
>>> random.randint(0, 1000)
607

在前面的示例中,首先调用RANTIN()。因为它不是Python内置函数的一部分,解释器对它一无所知,所以抛出了一个错误。

但是,在导入包含各种随机数生成函数的random库之后,可以通过点命名法显式调用randint(),即random.randint()。这就告诉Python解释器在random库中查找RANTIN(),从而得到期望的结果。

更清楚地说,当将模块导入程序中时,Python会假定一些命名空间。如果执行正常导入,即import foo,则主程序和foo都保持它们各自的命名空间。要使用foo模块中的函数,必须使用点命名法——以foo.bar()明确地标识它。

另一方面,如果模块的一部分是从foo导入(from foo impor bar)的,那么导入的组件就成为主程序命名空间的一部分。如果所有组件都是使用通配符(from foo import *)导入的,也会发生这种情况。

下面的示例显示了这些操作中的属性。

>>> from random import randint
>>> randint(0, 10)
2
>>> randrange(0, 25)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'randrange' is not defined

在前面的示例中,来自random库的randint()函数由它自身导入,它将randint()放入主程序的命名空间中。它允许直接调用randint(),而不必将其定义为random.randint()。但是,当尝试用randrange()函数执行相同的操作时会发生错误,因为它没有被导入。