Python基础指南之from-import与import的区别和常见陷阱详解

 更新时间:2026年07月27日 08:30:33   作者:星河耀银海  
本文深入解析了Python中import与from import的本质区别,包括命名空间、变量绑定和模块重载等底层机制,帮你避开from import的常见陷阱,掌握选型决策方法,写出更安全的代码

一、开篇:看起来一样,实际上不同

很多初学者认为import mathfrom math import sqrt只是"写法的区别"——一种要多写模块名,一种不用。但实际上,两种方式在命名空间、变量绑定、模块重载等底层行为上有本质区别。

先看一个让人困惑的例子:

# 场景:一个模块中有一个可变的配置变量
# config.py 内容:
# settings = {"debug": False}

# 方式一:import config
import config
cfg1 = config.settings  # cfg1引用config模块的settings

# 方式二:from config import settings
from config import settings as cfg2  # cfg2也是settings

# 修改源模块中的值
config.settings = {"debug": True, "new_key": "value"}

# 检查结果
print(cfg1)  # {'debug': True, 'new_key': 'value'}  ← 看到了新值!
print(cfg2)  # {'debug': False}  ← 还是旧值!

# 这就是 from import 的关键陷阱:
# from import 导入的是"那一刻的值引用",不会跟随源模块更新

这篇文章,我们深入探讨importfrom import的底层差异、常见陷阱以及选型决策。

二、命名空间的本质区别

2.1 import导入了什么

# import 模块名
# 导入的是整个模块对象,在当前命名空间中创建一个指向该模块的变量

import math

# math 是当前模块中的一个变量,指向math模块对象
print(math)       # <module 'math' (built-in)>
print(type(math)) # <class 'module'>

# 通过math访问模块中的一切
print(math.pi)
print(math.sqrt)

# import 导入了模块的"命名空间"
# 不会把模块内部的名称暴露到当前命名空间
# 所以不会有命名冲突

2.2 from import导入了什么

# from 模块名 import 名称
# 导入的是特定名称,在当前命名空间中创建指向该名称的变量

from math import sqrt, pi

# sqrt和pi是当前模块中的变量
print(sqrt)  # <built-in function sqrt>
print(pi)    # 3.141592653589793

# math本身没有被导入
# print(math)  # NameError: name 'math' is not defined

# 💡 关键理解:
# from math import sqrt 本质上做了:
# import math       # 1. 导入整个模块
# sqrt = math.sqrt  # 2. 创建局部变量sqrt,指向math.sqrt
# del math          # 3. 删除math模块的引用(但模块仍在sys.modules中)

2.3 内存角度对比

import sys

# 两种方式的内存占用
# import 模块 —— 导入整个模块对象(包含所有函数、类、变量)
import math
print(sys.getsizeof(math))  # 模块对象本身很小

# from 模块 import 名称 —— 同样导入了整个模块!
from math import sqrt
# math模块仍然被加载到了sys.modules中
print("math" in sys.modules)  # True
# 只是当前命名空间中没有math这个变量名而已

# 💡 from import 不会省内存!
# 无论哪种导入方式,整个模块都会被加载到内存
# 区别只是当前命名空间中有什么名字而已

三、变量绑定的经典陷阱

3.1 from import 是"快照"而非"引用"

# 模拟两个模块来演示陷阱

# ===== counter.py =====
"""
count = 0

def increment():
    global count
    count += 1
    return count
"""

# ===== test_import.py =====
# import counter  # 方式一
# from counter import count, increment  # 方式二

# 创建临时文件来演示
import tempfile, os

# 创建counter模块
counter_code = '''
count = 0

def increment():
    global count
    count += 1
    return count
'''
# 省略文件创建细节——实际使用时这样做:
# 1. 创建 counter.py 包含上述代码
# 2. 在另一个文件中测试:

# import counter
# print(counter.count)      # 0
# print(counter.increment()) # 1
# print(counter.count)      # 1 —— 正确!

# from counter import count, increment
# print(count)              # 0
# print(increment())        # 1
# print(count)              # 0 —— 错误!count没有变化!
# 因为 from import 的count只是导入时的值(整数0)的副本
# 而模块中的count已经变成了1

3.2 可变对象的特殊情况

# 如果导入的是可变对象(list, dict等),修改其内容
# 两种方式都能看到变化——因为修改的是同一个对象

# ===== data_module.py =====
"""
items = []  # 列表——可变对象

def add_item(item):
    items.append(item)
"""

# import data_module
# data_module.add_item("a")
# print(data_module.items)  # ['a']

# from data_module import items, add_item
# add_item("b")
# print(items)  # ['b'] —— 看到了!
# 因为items指向的是同一个列表对象

# 💡 但如果是给变量重新赋值:
# data_module.items = ["new"]  # 修改模块中的引用
# 用from import的items变量仍然指向原来的列表

四、模块重载时的行为差异

4.1 reload对两种导入方式的影响

import importlib

# 假设有一个模块 undergoing_development.py
# 你在开发中频繁修改它,希望重新加载

# 方式一:import 模块
import undergoing_development as ud
ud = importlib.reload(ud)  # ✅ ud现在指向重新加载后的模块
# 之后通过ud.xxx访问的都是新版本的

# 方式二:from import
from undergoing_development import my_function
# importlib.reload(undergoing_development) 没有用
# 因为my_function仍然指向旧版本的函数对象!

# 💡 开发建议:
# 如果你需要频繁重载模块(调试、开发阶段),用 import 模块名
# 不要用 from import

五、循环导入时的差异

# 循环导入时,两种方式的表现不同

# ===== module_a.py =====
# print("A开始")
# from module_b import func_b  # 可能报错!因为B还没完全加载
# def func_a():
#     return "A"

# ===== module_b.py =====
# print("B开始")
# import module_a  # 使用 import(而不是from import)
# def func_b():
#     return module_a.func_a()  # 延迟访问:在调用时才查找

# ✗ from import:在导入时就需要名称存在 → 更容易报错
# ✓ import 模块名:在访问时才查找名称 → 对循环导入更宽容

# ✅ 解决循环导入的方法:
# 1. 把import放在函数内部(延迟导入)
# 2. 重构——提取公共依赖
# 3. 使用 import 模块名 而不是 from import

六、选型决策

使用 import 模块名 的情况:

  • 需要使用模块中的多个功能
  • 模块名不长
  • 需要修改模块级变量并看到最新值
  • 需要重新加载模块
  • 存在循环导入的风险

使用 from import 的情况:

  • 只需要模块中的1-2个特定功能
  • 被导入的名称不会在模块中被修改
  • 需要代码简洁(如from collections import defaultdict)
  • 导入子模块(from os import path)

 一个实用的折中方案:

如果你想要简洁但又不想丢失命名空间信息:

from collections import defaultdict as dd  # 保留原名语义

但不推荐过度使用别名

七、总结

importfrom import的核心区别不在于功能(功能上都能完成任务),而在于变量绑定方式命名空间管理

核心要点:

  1. import X → 导入了模块对象,通过X.name访问,总是看到最新值
  2. from X import name → 导入了值的引用,如果源模块重新赋值,这里不会更新
  3. 两种方式的内存占用相同——整个模块都会被加载
  4. 默认选择import 模块名——更安全、更清晰
  5. 只在确实需要时使用from import——而且只导入少量名称

记住:from import 复制的是"那一刻的指针值",不是"对源模块变量的实时引用"。这是所有陷阱的根源。

到此这篇关于Python基础指南之from-import与import的区别和常见陷阱详解的文章就介绍到这了,更多相关Python from-import与import内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python如何管理多个PostgreSQL数据库的连接

    Python如何管理多个PostgreSQL数据库的连接

    这篇文章主要为大家详细介绍了Python如何通过读取配置文件中的PostgreSQL服务信息,连接到相应的PostgreSQL数据库,感兴趣的小伙伴可以了解下
    2024-11-11
  • Python使用内存缓存实例分享

    Python使用内存缓存实例分享

    Python中的内存缓存是一种将计算结果存储在内存中,以便在后续调用时快速获取结果的技术。通过使用装饰器和字典等数据结构,可以轻松实现内存缓存功能,提高程序的执行效率。
    2023-09-09
  • python装饰器相当于函数的调用方式

    python装饰器相当于函数的调用方式

    今天小编就为大家分享一篇python装饰器相当于函数的调用方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • python 借助numpy保存数据为csv格式的实现方法

    python 借助numpy保存数据为csv格式的实现方法

    今天小编就为大家分享一篇python 借助numpy保存数据为csv格式的实现方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • python安装numpy和pandas的方法步骤

    python安装numpy和pandas的方法步骤

    这篇文章主要介绍了python安装numpy和pandas的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • Python中使用绝佳的八个Debug 工具

    Python中使用绝佳的八个Debug 工具

    在Python开发中,调试是解决问题和提高代码质量的关键,有许多强大的调试工具可帮助开发者更快速地发现和解决问题,本文将介绍8个出色的Python调试工具,并提供详细的示例代码,让你更好地了解它们的用法和优势
    2024-01-01
  • python常见打开文件的方式示例详解

    python常见打开文件的方式示例详解

    文章介绍了Python中打开文件的两种方式open()和with open(),以及三个第三方库Pandas、NumPy和Pillow用于处理不同类型文件的读取方法,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2026-02-02
  • Python中的字典类型详解

    Python中的字典类型详解

    在Python中,字典是一种无序且可变的数据类型,用于存储键值对数据,字典可以根据键(key)来快速检索对应的值,相比于其他类型的容器,如列表(List)、元组等,字典具有更高效的查找和插入性能,因此在Python编程中被广泛使用
    2023-06-06
  • Python处理XML格式数据的方法详解

    Python处理XML格式数据的方法详解

    这篇文章主要介绍了Python处理XML格式数据的方法,结合实例形式详细分析了Python针对xml格式文件的编码处理、常见错误及相关操作注意事项,需要的朋友可以参考下
    2017-03-03
  • 零基础写python爬虫之使用urllib2组件抓取网页内容

    零基础写python爬虫之使用urllib2组件抓取网页内容

    文章详细介绍了在python2.5环境下,如何使用urllib2这个python自带的组件进行抓取指定网页内容的,整个过程记录的非常的详细,也很简单,有需要的朋友可以参考下,写出自己的python爬虫
    2014-11-11

最新评论