一、开篇:同一种需求,四种写法
python提供了四种导入模块的方式,每种都有不同的适用场景:
# 方式一:导入整个模块 import math # 方式二:导入模块并起别名 import math as m # 方式三:从模块中导入特定名称 from math import sqrt, pi # 方式四:导入模块中的所有公开名称 from math import *
这四种方式表面上看都能让你使用math模块的功能,但它们在命名空间、内存占用、代码可读性和潜在风险上有着本质区别。选择哪种方式,直接影响代码的质量。
二、四种方式逐一详解
2.1 方式一:import 模块名
# import 模块名 —— 导入整个模块对象 import math # 这创建了一个变量 math,指向整个math模块对象 print(type(math)) # <class 'module'> print(dir(math)[:10]) # ['__doc__', ..., 'acos', 'acosh', ...] # 使用:必须通过模块名访问 print(math.pi) # 3.141592653589793 print(math.sqrt(16)) # 4.0 print(math.sin(math.pi / 2)) # 1.0 # ✅ 优点: # 1. 命名空间清晰——math.sqrt一看就知道哪来的 # 2. 不会污染当前命名空间 # 3. 不会和已有的变量名冲突 # ❌ 缺点: # 1. 每次都写模块名——如果模块名很长会比较啰嗦 # 2. 导入了整个模块(虽然通常不是问题) # 💡 适用场景: # - 大多数情况下的默认选择 # - 模块名不太长时 # - 需要模块中多个功能时
2.2 方式二:import 模块名 as 别名
# import 模块名 as 别名 —— 给模块起一个短名字 import numpy as np import pandas as pd import matplotlib.pyplot as plt import tensorflow as tf # 使用别名访问 arr = np.array([1, 2, 3, 4, 5]) print(np.mean(arr)) # 3.0 # ✅ 优点: # 1. 输入更少(某些库的名字本身就很长) # 2. 符合社区约定(np是指numpy几乎已成标准) # 3. 解决命名冲突(两个模块有相同的名字) # ❌ 缺点: # 1. 如果用了非标准的别名,降低可读性 # import math as pineapple # 不要这样! # pineapple.sqrt(16) # 这段代码会让同事困惑 # ❌ 不推荐的用法 # 除非有明确的重命名理由(如避免冲突),否则不要随意起别名 import json as js # 不推荐——json本身就不长 # ✅ 推荐的用法(社区约定) import numpy as np # 标准约定 import pandas as pd # 标准约定 import matplotlib.pyplot as plt # 标准约定
2.3 方式三:from 模块名 import 名称
# from 模块名 import 名称 —— 将特定名称导入当前命名空间
from math import sqrt, pi, sin, cos
# 可以直接使用这些名称,不需要模块名前缀
print(sqrt(16)) # 4.0
print(pi) # 3.141592653589793
print(sin(pi / 2)) # 1.0
# 未导入的名称仍然需要通过模块名访问
# print(math.ceil(3.14)) # nameerror! math没有被导入
import math # 如果需要其他功能,单独导入模块
print(math.ceil(3.14)) # 4
# 可以同时导入多个名称
from os.path import join, exists, isfile
print(join("dir", "file.txt")) # dir/file.txt
# ✅ 优点:
# 1. 代码更简洁——不需要写模块名
# 2. 明确导入了什么——读者知道你用了模块的哪些功能
# 3. 可以只导入需要的——虽然不会减少内存占用
# ❌ 缺点和陷阱:
# 1. 可能和已有的变量名冲突
pi = "我自定义的pi"
from math import pi # 覆盖了上面的pi!
print(pi) # 3.141592653589793 —— 不是 "我自定义的pi"!
# 2. 导入太多名称会让命名空间混乱
from math import sin, cos, tan, asin, acos, atan, sqrt, pi, e, floor, ceil, ...
# 如果都是从不同模块导入的,阅读者很难知道每个名称来自哪里
# 3. 变量绑定问题——from import导入的是值的引用
# 如果源模块中的值改变了,from导入的变量不会自动更新
2.4 方式四:from 模块名 import *
# from 模块名 import * —— 导入模块的所有"公开"名称
# ⚠️ 这是最不推荐的方式!原因如下:
# 1. 你不知道导入了什么
from math import *
# 现在你有:sin, cos, tan, sqrt, pi, e, floor, ceil, ...
# 以及你不经意间覆盖的:pow, abs, sum, ...(任何同名变量)
# 2. 污染命名空间——导致难以发现的命名冲突
def sin(x):
"""我自己写的sin函数:返回字符串"""
return "这不是正弦"
from math import * # 刚才定义的sin被覆盖了!
print(sin(0)) # 0.0 —— 不是 "这不是正弦"
# 3. 可读性极差
# 读代码的人不知道sum来自哪里——是内置的?math的?还是你定义的?
# from math import *
# from statistics import *
# sum(...) # 这是哪个sum?
# 4. pep 8强烈不推荐
# "wildcard imports (from <module> import *) should be avoided"
# ✅ 唯一可接受的例外:
# 在 __init__.py 中控制包的公开api
# mypackage/__init__.py:
# from .module_a import classa, func_b
# from .module_b import classc
# __all__ = ["classa", "func_b", "classc"]
2.5 __all__控制from import *的行为
# __all__是一个模块级变量
# 定义了 from module import * 时哪些名称会被导入
# 假设有一个模块 my_module.py:
"""
__all__ = ["public_func", "publicclass"] # 白名单
def public_func():
'''这个函数会被导出'''
pass
def _private_func():
'''下划线开头的默认不会被导出'''
pass
class publicclass:
'''这个类会被导出'''
pass
class _privateclass:
'''下划线开头的默认不会被导出'''
pass
internal_data = "不会被导出" # 不在__all__中
"""
# 使用:
# from my_module import *
# public_func() # ✅ 可用(在__all__中)
# publicclass() # ✅ 可用(在__all__中)
# _private_func() # ❌ 不可用(_开头,且不在__all__中)
# internal_data # ❌ 不可用(不在__all__中)
# 💡 __all__是一种显式声明模块"公开api"的方式
# 即使不用from import *,__all__也能作为文档告诉使用者哪些是公开的
三、四种方式的对比
# ⌨️ 完整对比 # 假设要使用math、os、json三个模块的功能 # 方式一:import —— 命名空间最清晰 import math import os import json # 使用:math.sqrt(), os.path.join(), json.dumps() # 一目了然——每个函数来自哪个模块 # 方式二:import as —— 最灵活 import numpy as np import pandas as pd # 使用:np.array(), pd.dataframe() # 在保持清晰的同时简化了输入 # 方式三:from import —— 在简洁和清晰间平衡 from math import sqrt, pi from os.path import join from json import dumps, loads # 使用:sqrt(), join(), dumps() —— 简洁 # 但读者需要记住sqrt来自math,join来自os.path # 方式四:from import * —— 简洁但危险的 # from math import * # from os import * # ⚠️ os没有__all__,会导入所有名称! # 不到万不得已不要用
四、最佳实践
# ✅ 推荐做法一:默认使用 import 模块名 import math import json import os # 清晰、安全、不会污染命名空间 # ✅ 推荐做法二:社区标准的别名 import numpy as np import pandas as pd import matplotlib.pyplot as plt # ✅ 推荐做法三:from import 特定名称——只导入几个时 from collections import defaultdict, counter from pathlib import path from typing import optional, list # ✅ 推荐做法四:导入子模块 import os.path # 导入子模块 from os import path # 另一种方式 # ❌ 避免的做法 # 1. 不要用 from module import *(除非在__init__.py中) # 2. 不要给模块起奇怪的别名 # 3. 不要同时用多种导入方式从同一个模块导入 # 不好的例子: # import math # from math import sqrt # 重复了
五、总结
四种导入方式各有适用场景。总体原则是:以代码可读性为第一优先。
选型速查:
| 方式 | 适用场景 | 风险 |
|---|---|---|
import math | 默认选择,使用模块中多个功能 | 低 |
import numpy as np | 长模块名,社区约定 | 低 |
from math import sqrt | 只用到1-2个功能 | 中——命名冲突 |
from math import * | 几乎只在__init__.py中 | 高——不推荐 |
黄金法则:宁可多写几个字符,也别让读代码的人猜每个名字来自哪里。
到此这篇关于python模块基础指南之import的四种导入方式全对比的文章就介绍到这了,更多相关python import模块导入内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论