Python模块基础

Python模块基础完整进阶笔记

一、模块核心概念

1.1 什么是模块

模块就是一系列相关功能的集合体,或者说模块就是存放一系列相关功能的地方。

模块有四种通用类别:

1、一个python文件就是一个模块,如果文件名为test.py,模块名为test
2、把一系列py文件组织到一个文件夹下,称之为包(注:文件夹下需要有一个__init__.py)
3、已被编译为共享库或者DLL的C或者C++扩展
4、用C语言编写并连接到python解释器的内置模块

模块有三种来源:

1、python为我们提供好的:内置模块,标准库
3、自定义的模块

1.2 为何要用模块

分为两类场景:

I:针对内置、标准库、第三方的模块,我们只需要拿来就用,无需自定义,这种拿来主义,可以极大地提升程序的开发效率

II:自定义的模块

程序模块化之后,组织结构会更加清晰、更方便维护

可以将程序各部分共用的功能提取到一个模块中来为大家共享使用,好处就是减少了代码冗余问题

二、import 导入方式详解

2.1 首次导入模块的三件事

首次导入模块会执行三个核心步骤,后续重复导入不会重复执行代码:

1、首次导入模块会触发模块对应文件的运行

2、会产生一个该模块的名称空间,用来将模块文件运行过程产生的名字都丢到该名称空间中

3、会在当前名称空间中产生一个与模块同名的名字,该名字指向模块的名称空间

示例:被导入的test.py模块内容

def f1():
    pass

def f2():
    pass

def f3():
    pass

print("02 import导入模块的用法.py....")
x = 111
y = 333
z = 444

导入语句示例:

# 首次导入,执行上述三件事
import test

# 之后的导入,都是直接引用首次导入产生的模块名称空间,不会重复执行代码
# import test
# import test
# import test
# import test

2.2 模块名字的引用规则

使用「模块名.名字」的方式访问模块中的内容,有两个核心强调点:

强调1:模块名.名字,是指名道姓地向某一个名称空间/模块要名字,所以肯定不会与当前名称空间中的名字冲突。

# import spam
# money = 123
# print(spam.money)
# print(money)

# def read1():
#     print('02 import导入模块的用法.py---read1')
# print(spam.read1)
# spam.read1()
# read1()

# print(spam.read2)
# print(spam.change)

强调2:名称空间的关系是以定义阶段为准的,与调用位置无关,即无论在哪调用,都要回到当初定义的位置去查作用域关系。

# 例1
# money = 123
# spam.read1()

# 例2
# def read1():
#     print('02 import导入模块的用法.py...read1')
# spam.read2()

# 例3
# money = 666
# spam.change()
# print(money)
# print(spam.money)

2.3 多模块导入规范

可以以逗号为分隔符在一行导入多个模块,但不建议这么做,推荐分行导入。

# 建议的导入方式
# import time
# import os
# import sys

# 不建议在一行同时导入多个模块
# import time,os,sys

导入位置规范:通常导入模块的操作都放在文件最开头、顶格写,摆放顺序建议:

I:python自带的模块

II:第三方模块

III:自定义模块

# import time
# import sys
#
# import 第三方1
# import 第三方2
#
# import 自定义模块1
# import 自定义模块2
# import 自定义模块3

2.4 给模块起别名

使用 import ... as xxx 可以给导入的模块起别名,简化调用,也用于统一不同模块的调用接口。

# import spam as sm
# print(sm.money)

应用案例:根据不同数据库类型统一调用接口

# db_type = input(">>>: ")
# if db_type == "mysql":
#     import mysql as db
# elif db_type == "oracle":
#     import oracle as db
#
# db.sqlparse()

2.5 模块的其他特性

1、模块也是第一类对象,可以赋值、传递、作为参数使用。

# import spam  # spam = 模块名称空间的地址

2、Python3中自定义模块的命名建议采用纯小写字母+下划线的风格。

3、可以在函数内导入模块,模块的名字会生成在函数的局部名称空间中,全局无法访问。

# def foo():
#     import time
#     print("foo====>",time.time())
#
# foo()
# time.time()  # 全局无法访问,会报错

三、from ... import ... 导入方式详解

3.1 导入的三件事

from ... import ... 导入也会发生三件事:

1、触发模块对应文件的运行

2、会产生一个模块的名称空间,用于将模块文件运行过程中产生的名字都扔进去

3、在当前名称空间中拿到一个或多个名字,这些名字直接指向模块名称空间中对应的名字

# from spam import money
# from spam import money  # 后续重复的导入不会重复执行文件,会直接引用之前产生的名称空间
# from spam import money

3.2 优缺点

优点:使用时不需要加前缀,代码更简洁

缺点:容易与当前名称空间中的名字冲突

# money = 666
# from spam import read1
# from spam import change
# from spam import money
#
# print(money)
# read1()
# change()
# print(money)
# read1()

3.3 导入多个名字与通配符导入

可以以逗号为分隔符在一行导入多个模块中的名字:

# from spam import money,read1,read2,change
# print(money)

使用 * 代表导入模块中的所有名字:

# from spam import *
# print(money)
# print(read1)
# print(read2)
# print(change)

补充:all 可以控制 * 导出的名字范围,在模块文件中定义__all__列表,列表中的名字才会被*导入。

示例:spam.py中定义

__all__ = ['money', 'read1']
money = 100
def read1():
    pass
def read2():
    pass
def change():
    pass

此时 from spam import * 只能导入money和read1,read2和change不会被导入。

3.4 给导入的名字起别名

可以给导入的单个或多个名字起别名:

# from spam import money as m,read1 as r,read2,change
#
# print(m)
# print(r)
# print(read2)
# print(change)

3.5 两种导入方式的适用场景

1、如果程序中要使用模块中的某一个名字很多次,import方式每次调用都加前缀会让代码显得啰嗦,此时用from ... import ...比较合适。

2、使用from ... import ... 必须注意避免名字冲突,导入的名字不要和当前作用域的名字重名。

from time import sleep
sleep(1)
sleep(1)
sleep(1)
sleep(1)
sleep(1)
sleep(1)
sleep(1)
sleep(1)
sleep(1)

四、模块的两种用途与 name 变量

一个python文件有两种用途:

1、可以被当成模块导入使用,此时内置变量__name__等于"模块名字"

2、可以被当成执行程序直接运行,此时内置变量__name__等于"main"

# 作为模块导入时的示例
# import test
# test.f1()
# import m1

基于这个特性,通常会在模块文件中编写如下判断,用于区分文件是被导入还是直接运行:

if __name__ == '__main__':
    # 直接运行文件时执行的代码,作为模块导入时不执行
    f1()
    print("文件直接运行")

五、循环导入问题

5.1 循环导入的根本原因

模块的循环/嵌套导入抛出异常的根本原因是:在python中模块被导入一次后,就不会重复导入了,只会在第一次导入时执行模块内的代码,后续的导入都是直接引用之前的结果。如果两个模块互相导入,而导入时对方的名字还没定义完成,就会报错。

补充说明:一个python文件有两种用途,一个是可以被当成执行文件执行,另外一个是可以被当成模块导入。python文件被当成执行文件首次执行时,并不代表它被导入了,一定是针对导入操作才有首次跟二次之分,所以一个文件是执行文件还是模块,背后的名称空间逻辑是不一样的。

5.2 循环导入的解决方法

1、真正的解决方案就是不要让程序有循环导入的设计,应该从架构上避免。

2、临时解决方案:

把引用的名字前提,保证导入之前已经有了,但这种方式解决不了根本问题。

把涉及到循环导入的语句都放到其使用的地方,比如某个函数内,延迟导入时机。

# import m1
# m1.f1()

六、模块查找路径与优先级

但凡导入模块,无论是import的导入方式,还是from...import的导入方式,都涉及到模块查找的问题。

6.1 查找路径与优先级

查找优先级从高到低依次为:

1、内存:已经加载到内存中的模块优先查找

2、内置模块:Python解释器内置的模块

3、sys.path:模块搜索路径列表

注意事项:

(1)执行程序所在的文件夹/目录默认就在sys.path的第一个位置

(2)pycharm为你添加到sys.path的路径应该当其不存在,生产环境不会有这些额外路径

(3)压缩包也相当于文件夹,可以作为模块路径

# import sys
# print(sys.path)

6.2 查找示例

例1:验证内存中已加载的模块不会重复加载

# import spam
# print("首次。。。:",spam.money)
#
# import time
# time.sleep(20)
#
# import spam
# print("二次。。。:",spam.money)

例2:sys.modules与模块名字删除

# import spam  # spam -》模块的名称空间的地址
# import sys
#
# print("spam" in sys.modules)
# del spam  # 只是删除当前名称空间中的名字,不是删除模块文件
# print("spam" in sys.modules)
#
# def func():
#     import spam
#     print(spam.money)
#
# func()

例3:手动添加模块搜索路径

# import sys
# sys.path.append(r"D:\project\day11")
# import spam

七、补充知识点

7.1 sys.modules

sys.modules可以用来查看已经加载到内存中的所有模块,是一个字典,key是模块名,value是模块对象。

# import sys
# print(sys.modules)
# print("time" in sys.modules)
# import spam
# print("spam" in sys.modules)

7.2 引用计数补充

可以通过sys.getrefcount查看对象的引用计数:

import sys
x = [1, 2, 3, 4]
print(sys.getrefcount(x))
posted @ 2026-07-16 21:26  cuckooyang  阅读(22)  评论(0)    收藏  举报