盒子
导航
文章目录
  1. 从模块中使用 * 导入模块成员
  2. 嗯,这太糟糕了,那么 __all__ 呢?
  3. 对于Package是怎样的结果?
  4. 但是,为什么这么做很不好?

Python[译]:关于使用import *

本文译自:https://shahriar.svbtle.com/importing-star-in-python

本文将讨论Python中的from <module> import *from <package> import *语句,它们的工作机制以及为何这种用法是很不好的Python编程风格。

从模块中使用 * 导入模块成员

from <module> import * 意在 “我想要访问到<module>中的所有成员”。现在,假定我们有个something.py程序,它的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# something.py

public_variable = 42
_private_variable = 141

def public_function():
print("I'm a public function! yay!")

def _private_function():
print("Ain't nobody accessing me from another module...usually")

class PublicClass(object):
pass

class _WeirdClass(object):
pass

Python解释器中,我们可以执行from something import * ,然后就会看到如下结果:

>>> from something import *
>>> public_variable
42
>>> _private_variable
...
NameError: name '_private_variable' is not defined
>>> public_function()
"I'm a public function! yay!"
>>> _private_function()
...
NameError: name '_private_function' is not defined
>>> c = PublicClass()
>>> c
<something.PublicClass object at ...>
>>> c = _WeirdClass()
...
NameError: name '_WeirdClass' is not defined

从上面的结果可以看到,from something import * 导入了something中所有除了以下划线开始的成员,因为这些以下划线开始的成员被视为私有的。

嗯,这太糟糕了,那么 __all__ 呢?

上文中没有讲到什么是__all__ __all__ 是一个字符串的list,它定义了在使用from <module> import * 语句导入该模块(module)或者包(package)时,其中的哪些成员可以被导出。 如果我们不显式地定义__all__ (例如上面的something.py中就没有定义),那么import * 的执行行为将会是导入该模块或者包当中的所有除了以下划线开始的成员:按照惯例,以下划线开始的成员符合被视为私有的,所以import * 有这个执行行为就很好理解了。接下来看看如果我们显式地声明了__all__,将会有怎样的结果。现在something.py如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# something.py

__all__ = ['_private_variable', 'PublicClass']

# The rest is the same as before

public_variable = 42
_private_variable = 141

def public_function():
print("I'm a public function! yay!")

def _private_function():
print("Ain't nobody accessing me from another module...usually")

class PublicClass(object):
pass

class _WeirdClass(object):
pass

现在,我们期望执行from something import * 的执行结果是只导入_private_variablePublicClass,结果如下:

>>> from something import *
>>> public_variable
...
NameError: name 'public_variable' is not defined
>>> _private_variable
0
>>> public_function()
...
NameError: name 'public_function' is not defined
>>> _private_function()
...
NameError: name '_private_function' is not defined
>>> c = PublicClass()
>>> c
<something.PublicClass object at ...>
>>> c = _WeirdClass()
...
NameError: name '_WeirdClass' is not defined

对于Package是怎样的结果?

从一个包中使用import * 导入时,__all__ 做的事情跟从module导入是差不多的,区别只在于它导入的是这个包中的module而已。所以,当使用from <package> import * __all__指定了这个包(package)中哪些模块(module)应该被导入到当前命名空间当中。

然而,区别在于,当你在一个包的__init__.py中声明了__all__时,from <package> import *将不会导入任何东西(这个其实不是很确定,可以参考这里寻找正确的结论)。

但是,为什么这么做很不好?

在你每次打算在Python解释器中这么做之前,请先阅读一遍The Zen of Python

Explicit is better than implicit.(外显胜于内隐)

from <module> import * 并不是显式的使用方式,它并没有告知我们将要导入哪些名字(names)到我们的命名空间当中。显式地导入我们需要的模块或者名字会是更好的选择,这对于后期代码维护也是很有好处的,这让后来者不会因为不知道哪个变量/函数/类/等等来自何处而感到困惑,这也很遵循Python的禅道

Readability counts.(可读性)

即使要导入的名字很多,显式地导入也会让代码更为清楚易读(参考PEP 328):

from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text, 
LEFT, DISABLED, NORMAL, RIDGE, END)

这么做能让你通过Ctrl+F查找很清楚地知道代码中所使用的某个名字(变量、函数、类等等)来自于哪儿。另外,隐式地导入所有会让你的代码隐藏在潜在的危险之中:如果一个模块或者包的作者修了__all__的内容呢?具体情况如:

  1. 作者从__all__列表中移除了某些名字。如果你的代码使用了这些名字,你的代码将会抛出NameError异常,但是因为使用import * ,你很难找到它来自于哪儿。
  2. 作者向__all__列表中增加了很多名字,但是你的代码并不需要它们,但是它们却占据了你的命名空间的一部分名字,如果你后面新增的名字与其重名了呢?你将无法意识到。

当然,有时候使用import * 可能会有必要或者更为有用,但是这么做之前请确保的确有这个必要。在我的经验当中,这种做法通常在惰性加载当中才会使用,而不是随处使用。