与import有关的知识点(Python)

Last Updated: 2024-05-09 13:36:34 Thursday

-- TOC --

star import

这个词我也不记得是在什么地方看到的了,它表示这样的import语句:

from <module|package> import *

一般我们是不推荐用这样的方式做import,就像一般我们不推荐在C语言中使用goto一样!

这种star import的方式将导入模块中所有的非下划线(_)开始的对象,这种全部导入的方式,有人说容易污染namespace,不过,还是有方法可以控制可导入对象的。

直接import,可以通过module或者package name访问所有全局对象;而star import,只会将所有非_开始的全局对象导入当前namespace,除非定义了__all__这个name list。

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

__all__

常常在模块中见到__all__ = [...]

在模块中定义此对象的作用,就是为了配合star import。

当有定义__all__的时候,star import只会导入在__all__中有定义的name!如果__all__中指定的对象没有被定义,star import的时候,会抛出AttributeError。直接import时,无视__all__

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

__all__中都是str对象。

__init__.py

import一个package的时候,package目录中的__init__.py文件会被执行,因此它很适合用来做package的初始化工作。

import不管是单文件的module,还是一个目录的package,都是执行一个py文件!只是__name__,分别是module name或者package name!

当一个目录下没有此文件时,import这个目录也能成功,但是没有任何效果,这个目录不是一个package,只是个有一堆py文件的目录,可以通过import a.b这种方式来import其中的module。

import a.b

这种语句,实际上import的是一个module c(如果c是一个py文件的话),只是这个module c的路径,是a/b。很多时候,a可能是个package,这种就是只import某个package中的module。

一个值得注意的细节,用这种方式import,c模块在运行时的__name__ == a.b

我测试了import a.b.c,没有成功!

交叉引用Python模块造成的Bug

有错误的测试代码

$ cat a.py
from b import d
class c: pass

$ cat b.py
from a import c
class d: pass

错误表现

$ python3 a.py
Traceback (most recent call last):
  File "a.py", line 1, in 
    from b import d
  File "/home/xinlin/test/b.py", line 1, in <module>
    from a import c
  File "/home/xinlin/test/a.py", line 1, in <module>
    from b import d
ImportError: cannot import name 'd' from 'b' (/home/xinlin/test/b.py)
$ python3 b.py
Traceback (most recent call last):
  File "b.py", line 1, in 
    from a import c
  File "/home/xinlin/test/a.py", line 1, in <module>
    from b import d
  File "/home/xinlin/test/b.py", line 1, in <module>
    from a import c
ImportError: cannot import name 'c' from 'a' (/home/xinlin/test/a.py)

错误原因分析

  1. 执行a.py中的from b import d,由于是执行的python a.py,此时在sys.modules中并没有module b存在,python解释器首先为b.py在sys.modules中创建一个module对象,注意,这时创建的这个module对象是空的,里边啥也没有。在Python内部创建了这个module对象之后,就会解析执行b.py,其目的是填充module b这个dict。
  2. 在执行b.py的过程中,首先会碰到这一句from a import c,此时检查sys.modules这个module缓存中是否已经存在module a了,类似的,Python内部会为a.py创建一个module对象,然后,同样地,执行a.py中的语句。
  3. 再次执行a.py中的from b import d,这时,由于在第1步时,创建的module b对象已经缓存在了sys.modules中,所以直接就得到了module b,但是请注意,从整个过程来看,我们知道,这时module b还是一个空的对象,里面啥也没有,所以从这个module中获得符号d的操作就会抛出异常。

修改方式

不要使用from ... import ...,直接import。

注意

每个模块,在Python解释器中,都只会真正地import一次,每个模块在内存中,只有一份!

本文链接:https://cs.pynote.net/sf/python/202301242/

-- EOF --

-- MORE --