Skip to main content

模块

info

绝对导入和相对导入为Python包的组织提供了清晰的规则。明确的导入方式让代码的依赖关系更加清晰,避免了命名冲突。

# 绝对导入
from mypackage.submodule import function

# 相对导入
from .submodule import function
from ..parentmodule import other_function

PEP 328 – 导入:多行和绝对/相对导入

模块

用模块管理函数,Python 中每个文件就代表了一个模块(module),

Python 会将所有 .py 结尾的文件认定为 Python 代码文件。

import,from,as

在使用函数的时候我们通过 import 关键字导入指定的模块:

module1.py

def foo():
print('hello, world!')

module2.py

def foo():
print('goodbye, world!')

test.py

from module1 import foo

# 输出hello, world!
foo()

from module2 import foo

# 输出goodbye, world!
foo()

name属性

有时候我们想将一个 .py 文件既当作脚本,又能当作模块用, 这个时候可以使用 name 这个属性。

PI = 3.14


def get_sum(lst):
"""
Sum the values in the list
:param lst:
:return:
"""
total = 0
for v in lst:
total = total + v
return total

上文保存为 ex.py

with open('ex.py', 'w') as f:
f.write("""
PI = 3.14
def get_sum(lst):
total = 0
for v in lst:
total = total + v
return total
""")

使用 ! 调用 shell 命令:

!cat ex.py

可以从 ex 模块中导入函数 get_sum 和变量:

from ex import PI, get_sum

print(PI) # 3.14
print(get_sum([2, 3])) # 5

# 可以使用 * 导入所有变量, 不提倡,因为可能覆盖一些已有的函数
# 删除文件:
import os

os.remove('ex.py')

模块导入顺序

通常情况下,当使用 import 语句导入模块后,Python 会按照以下顺序查找指定的模块文件:

前目录,即当前执行的程序文件所在目录下查找;

到 PYTHONPATH(环境变量)下的每个目录中查找;

到 Python 默认的安装目录下查找。

以上所有涉及到的目录,都保存在标准模块 sys 的 sys.path 变量中,通过此变量我们可以看到指定程序文件支持查找的所有目录。换句话说,如果要导入的模块没有存储在 sys.path 显示的目录中,那么导入该模块并运行程序时,Python 解释器就会抛出 ModuleNotFoundError(未找到模块)异常。

解决“Python 找不到指定模块”的方法有 3 种,分别是:

向 sys.path 中临时添加模块文件存储位置的完整路径;

将模块放在 sys.path 变量中已包含的模块加载路径中;

设置 path 系统环境变量。

具体区别可以创建下面的文件解构来了解:

top/
├── __init__.py
├── second.py
└── second_copy.py
second.py
import sys
print(sys.argv)
from .second_copy import *
second_copy.py
import sys
print(sys.argv)

两种运行脚本的方式,以及对应的输出:

  1. 使用 python -m top.second 运行

    PS C:\Users\jiang\Desktop> python -m top.second
    ['C:\\Users\\jiang\\Desktop\\top\\second.py']
    ['C:\\Users\\jiang\\Desktop\\top\\second.py']
    • 输出两次相同的 sys.argv,显示脚本的完整路径。
    • 没有错误,脚本正常运行。
  2. 直接运行 python top\second.py

    PS C:\Users\jiang\Desktop> python top\second.py
    ['top\\second.py']
    Traceback (most recent call last):
    File "C:\Users\jiang\Desktop\top\second.py", line 3, in <module>
    from .second_copy import *
    ImportError: attempted relative import with no known parent package
    • 仅输出了 sys.argv 一次,显示的是相对路径 top\second.py
    • 然后抛出了 ImportError,提示“尝试进行相对导入,但没有已知的父包”。

为什么会有这样的差异?

问题的核心在于 Python 如何处理这两种运行方式,以及它们对模块结构和相对导入的影响。

1. python -m top.second 的行为
  • 运行方式:使用 -m 标志告诉 Python 将 top.second 作为一个模块运行。这里,top 被识别为一个包,second 是该包中的一个模块。
  • 包上下文:Python 会正确设置包的层次结构。由于当前工作目录是 C:\Users\jiang\Desktop,Python 知道 top 是一个包,并且 second.py 是其中的模块。
  • 相对导入:在 second.py 中,from .second_copy import * 是一个相对导入,. 表示当前包(即 top)。因为 Python 已经建立了包上下文,它能找到同一目录下的 second_copy.py,导入成功。
  • sys.argv 的值:在这种模式下,sys.argv[0] 被设置为脚本的完整路径,即 C:\\Users\\jiang\\Desktop\\top\\second.py
    • 首先,second.py 打印这个值。
    • 然后,导入 second_copy.py 时,second_copy.py 也打印 sys.argv,因为 sys.argv 是全局的,不会因模块不同而改变,所以输出两次相同的结果。
2. python top\second.py 的行为
  • 运行方式:直接通过文件路径运行 second.py,即将其作为独立的脚本执行,而不是作为一个包中的模块。
  • 包上下文缺失:在这种情况下,Python 不会将 top 视为一个包,而是直接运行 second.py 作为主模块(__main__)。因此,没有定义任何“父包”。
  • 相对导入失败second.py 中的 from .second_copy import * 依赖于包结构,但由于缺少包上下文,Python 不知道 . 代表什么,导致抛出 ImportError: attempted relative import with no known parent package
  • sys.argv 的值:在这里,sys.argv[0] 是命令行中提供的路径,即 top\second.py(相对于当前工作目录 C:\Users\jiang\Desktop)。
    • second.py 打印这个值后,尝试执行相对导入时失败,因此程序终止,second_copy.py 的代码未被执行。

垃圾回收机制

如果持续不断加载数据,调用函数模块,计算机的内存会溢出,Python 的垃圾回收机制。是计数机制,当一个对象的引用数为 0 时,它就会被垃圾回收机制回收。

import sys
# 生以下四种情况的时候,该对象的引用计数器+1
a= 999 # 对象被创建  
b=a # 对象被引用 
def func(a):
return
func(a) # 对象被作为参数,传到函数中
List=[a,"a","b",2] # 对象作为一个元素,存储在容器中  
sys.getrefcount(a)
# python系统内部很多地方都在使用一些常用的对象,这些对象在python解释器启动时就被创建出来。

#发生以下四种情况时,该对象的引用计数器**-1**

#该对象的别名被显式销毁时 
del a
#该对象的引别名被赋予新的对象,  
a = 999
#个对象离开它的作用域,例如 func函数执行完毕时,函数里面的局部变量的引用计数器就会减一(但是全局变量不会)
#该元素从容器中删除时,或者容器被销毁时。
b = a # 当前计数器为2
del b # 删除变量b:b对应的对象的引用计数器-1 (此时计数器为1)
del a # 删除变量a:a对应的对象的引用计数器-1 (此时引用计数器为0)

# 当引用计数器为0 时,意味着没有人再使用这个对象,这个对象就变成垃圾,垃圾回收。
# 回收:1.对象从refchain的链表移除。
#.将对象进行销毁,内存归还给操作系统,可用内存就增加。
sys.getrefcount(a)

内置函数

__import__函数

函数签名:__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module

参数说明:

  • name:要导入的模块名
  • globals:全局命名空间
  • locals:局部命名空间
  • fromlist:要导入的属性列表
  • level:导入级别

返回值:

  • 返回导入的模块
math = __import__('math')

print(math.sqrt(16)) # 4.0