from module import * 中的细节

2019年10月31日 / 417次阅读 / Last Modified 2021年4月22日
语法

在python中导入模块,一般就是直接import module。另一种方式是from module import *,这种方式将导入模块中所有的非下划线(_)开始的对象,这种全部导入的方式,有人说容易污染namespace,不过,还是有方法可以控制可导入对象的。

一个值得注意的细节:import只能跟module或package,而from ... import可以跟函数,类,变量等对象!

直接import

直接import一个模块,那么这个模块所有的对象,都可以通过模块名来访问,因此不管名称有无下划线,统统都可以访问。

$ cat all.py
a = 1
b = 2
_c = 3
__d = 4

def aa(): pass
def bb(): pass
def _cc(): pass
def __dd(): pass

class aaa(): pass
class bbb(): pass
class _ccc(): pass
class __ddd(): pass

all.py这个文件中,有一些带下划线的对象,我们在python解释器中直接import all看一下效果:

>>> import all
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'all']
>>> dir(all)
['__builtins__', '__cached__', '__d', '__dd', '__ddd', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_c', '_cc', '_ccc', 'a', 'aa', 'aaa', 'b', 'bb', 'bbb']

all中所有的对象,都要通过all这个模块名来访问,因此直接调用dir()函数,只能看到模块all,要使用dir(all),才能看到所有的对象。可以发现,不管是一个下划线,还是两个下划线,所有的对象都可以通过all模块来访问。

from module import * 的效果

>>> from all import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'aa', 'aaa', 'b', 'bb', 'bbb']

所有带下划线的对象,都没有导入进来。from module import * 不会导入带下划线的对象。

使用from的另一个细节是:对象直接导入到上层namespace中,对象直接通过自己的名字就可以访问,这时要注意名字冲突!

如何控制导入对象

在使用from的情况下,有两种方式可以控制具体导入什么对象。

列出具体的对象名

>>> from all import a,bb,_cc
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_cc', 'a', 'bb']

列出具体的对象名,就可以在from的时候,导入带下划线的对象。

使用__all__属性

修改一下all.py文件,增加一个__all__属性:

$ cat all.py

__all__ = ['a','b','_cc','__ddd']

a = 1
b = 2
_c = 3
__d = 4

def aa(): pass
def bb(): pass
def _cc(): pass
def __dd(): pass

class aaa(): pass
class bbb(): pass
class _ccc(): pass
class __ddd(): pass

注意__all__这个list中的元素,要带引号。

这个时候,再使用from加*,就只能import __all__这个list中的对象,起到控制的作用:

>>> from all import *
>>> dir()
['__annotations__', '__builtins__', '__ddd', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_cc', 'a', 'b']

这种用法,也可以导入带下划线的对象。

如果是写一个模块给别人用的,最好是加上__all__这个属性,控制一下比较好。自己写代码,采用列出对象名的显式的方式,会比较清晰。一般不建议使用from module import *这个方式。

-- EOF --

本文链接:https://www.pynote.net/archives/1417

留言区

《from module import * 中的细节》有3条留言

您的电子邮箱地址不会被公开。 必填项已用*标注

  • 麦新杰

    用star import存在一个问题:再用flake8做lint的时候,undefined name就无法检查了,而这个undefined name对于像python这样解释执行的语言来说,是个很重要的潜在风险点。因为undefined name只有在执行到那一行代码的时候,才知道是否正确,测试就要求100%分支覆盖,这在很多时候是比较困难和耗时的。 [回复]

  • 麦新杰

    star import [回复]

  • 麦新杰

    如果__all__中带单引号的对象没有被定义,from引入的时候,会抛出AttributeError。直接import时,无视__all__。 [回复]


前一篇:
后一篇:

More

麦新杰的Python笔记

Ctrl+D 收藏本页


©Copyright 麦新杰 Since 2019 Python笔记

go to top