基本概念
**模块 module:**一般情况下,是一个以.py为后缀的文件
①Python内置的模块(标准库);
②第三方模块;
③自定义模块。包 package:
当一个文件夹下有 init .py时,意为该文件夹是一个包(package),其下的多个模块(module)构成一个整体,而这些模块(module)都可通过同一个包(package)导入其他代码中。
- 其中 init .py文件 用于组织包(package),方便管理各个模块之间的引用、控制着包的导入行为
- from pacakge_1 import *这种形式的写法,需在 init .py中加上: all = [‘file_a’, ‘file_b’]。 all 是一个重要的变量,用来指定此包(package)被import *时,哪些模块(module)会被import进【当前作用域中】
- path 也是一个常用变量,是个列表,默认情况下只有一个元素,即当前包(package)的路径
sys.modules、命名空间、模块内置属性
sys.modules 是一个 将模块名称(module_name)映射到已加载的模块(modules) 的字典。可用来强制重新加载modules。Python一启动,它将被加载在内存中。当我们导入新modules,sys.modules将自动记录下该module;当第二次再导入该module时,Python将直接到字典中查找,加快运行速度。
命名空间
- 每个函数function 有自己的命名空间,称local namespace,记录函数的变量。
- 每个模块module 有自己的命名空间,称global namespace,记录模块的变量,包括functions、classes、导入的modules、module级别的变量和常量。
- build-in命名空间,它包含build-in function和exceptions,可被任意模块访问。
查找顺序:local namespace ->global namespace ->build-in namespace
对于闭包,若在local namespace找不到该变量,则继续查找父函数的local namespace。
内置函数locals()、globals()返回一个字典。区别:前者只读、后者可写
from module_A import X VS import module_A(语言理解)
内置属性
- name 直接运行本模块, name 值为 main ;import module, name 值为模块名字。
- file 当前 module的绝对路径
- dict
- doc
- package
- path :当前包(package)的路径
导入模块
绝对导入
绝对导入:所有的模块import都从“根节点”开始。根节点的位置由sys.path中的路径决定,项目的根目录一般自动在sys.path中。如果希望程序能处处执行,需手动修改sys.path
import sys,os BASE_DIR = os.path.dirname(os.path.abspath(__file__))#存放c.py所在的绝对路径 sys.path.append(BASE_DIR) from B.B1 import b1#导入B包中子包B1中的模块b1
相对导入
只关心相对自己当前目录的模块位置就好。不能在包(package)的内部直接执行(会报错)。
常见错误❌ValueError: Attempted relative import beyond toplevel package
单独导入包(package)
c.py:
import B
B.B1.b2.print_b2()
Error 报错 ❌
①一个.py文件调用另一个.py文件中的类。
如 a.py(class A)、b.py(class B),a.py调用b.py中类B用:from b import B
②一个.py文件中的类 继承另一个.py文件中的类。如 a.py(class A)、b.py(class B),a.py中类A继承b.py类B。
导入包
和模块导入基本一致,只是导入包时,会执行这个 init .py,而不是模块中的语句。
- 如果**只是单纯地导入包【形如:import xxx】**,而包的 init .py中有没有明确地的其他初始化操作,则:此包下的模块 是不会被自动导入的。当然该包是会成功导入的,并将包名称放入当前.py的Local命名空间中。
import与_import_
在Python中,__import__
是一个内置函数,用于动态地导入模块。它的使用方式如下:
__import__(name, globals=None, locals=None, fromlist=(), level=0)
name
是要导入的模块名称。globals
是一个字典,用于指定导入过程中的全局命名空间。如果不提供,则使用当前全局命名空间。locals
是一个字典,用于指定导入过程中的局部命名空间。如果不提供,则使用globals
。fromlist
是一个可选参数,如果指定了,它是一个包含要导入的子模块或对象名称的列表。level
是相对导入的级别。如果指定了level
,则name
必须是相对于当前模块的路径。如果未指定,则默认为0,表示标准的绝对导入。
通常情况下,推荐使用
import
语句来导入模块,而不是直接使用__import__
函数,因为import
语句更加直观和易于理解。但是,在某些特殊情况下,__import__
可能会更灵活,例如需要在运行时动态地确定要导入的模块名称时。directory = os.path.dirname(model_url) sys.path.append(directory) basename = model_url.split('\\')[-1] # 将路径按照反斜杠分割,并取最后一个部分 filename = basename.split('.')[0] # 将文件名按照点分割,并取第一个部分 module=__import__(filename)
理解Python在执行import语句
- 创建一个新的、空的module对象(它可能包含多个module);
- 将该module对象 插入sys.modules中;
- 装载module的代码(如果需要,需先编译);
- 执行新的module中对应的代码。
在执行step3时,首先需找到module程序所在的位置,如导入的module名字为mod_1,则解释器得找到mod_1.py文件,搜索顺序是:
当前路径(或当前目录指定sys.path)--->PYTHONPATH---->Python安装设置相关的默认路径。
对于不在sys.path中,一定要避免用import导入 自定义包(package)的子模块(module),而要用from…import… 的绝对导入 或相对导入,且包(package)的相对导入只能用from形式。
顺序导入
(Local命名空间独立,无传递!)
from moduleB import ClassB有两个过程,先from module,后import ClassB
循环导入
解决方案
方法1----->延迟导入(lazy import):把import语句写在方法/函数里,将它的作用域限制在局部。(此法可能导致性能问题)
方法2----->将from x import y改成import x.y形式
方法3----->组织代码(重构代码):更改代码布局,可合并或分离竞争资源。
合并----->都写到一个.py文件里;
分离–>把需要import的资源提取到一个第三方.py文件中。
总之,将循环变成单向。
多数知识图源自:模块管理-预备知识-CSDNPython入门技能树