1+ #!/usr/bin/env python3
2+ # -*- coding: utf-8 -*-
3+ """
4+ 模块搜索路径演示
5+
6+ 本文件演示Python如何搜索和定位模块,包括:
7+ 1. sys.path的作用和内容
8+ 2. 模块搜索顺序
9+ 3. 动态修改搜索路径
10+ 4. PYTHONPATH环境变量
11+ 5. .pth文件的使用
12+ 6. 包的搜索机制
13+
14+ 学习目标:
15+ - 理解Python模块搜索机制
16+ - 掌握如何查看和修改搜索路径
17+ - 了解不同搜索路径的优先级
18+ - 学会解决模块导入问题
19+ """
20+
21+ import sys
22+ import os
23+ from pathlib import Path
24+
25+ # ============================================================================
26+ # 1. 查看当前的模块搜索路径
27+ # ============================================================================
28+
29+ def show_current_search_paths ():
30+ """
31+ 显示当前的模块搜索路径
32+ """
33+ print ("=== 1. 当前模块搜索路径 ===" )
34+ print ("Python在以下路径中按顺序搜索模块:\n " )
35+
36+ for i , path in enumerate (sys .path , 1 ):
37+ print (f"{ i :2d} . { path } " )
38+
39+ # 检查路径是否存在
40+ if os .path .exists (path ):
41+ path_type = "目录" if os .path .isdir (path ) else "文件"
42+ print (f" └─ [{ path_type } ] 存在" )
43+ else :
44+ print (f" └─ [不存在]" )
45+
46+ print (f"\n 总共 { len (sys .path )} 个搜索路径" )
47+
48+ # ============================================================================
49+ # 2. 分析搜索路径的来源
50+ # ============================================================================
51+
52+ def analyze_path_sources ():
53+ """
54+ 分析各个搜索路径的来源
55+ """
56+ print ("\n === 2. 搜索路径来源分析 ===" )
57+
58+ # 当前工作目录
59+ current_dir = os .getcwd ()
60+ print (f"当前工作目录: { current_dir } " )
61+
62+ # 脚本所在目录
63+ script_dir = os .path .dirname (os .path .abspath (__file__ ))
64+ print (f"脚本所在目录: { script_dir } " )
65+
66+ # Python安装目录
67+ python_home = sys .prefix
68+ print (f"Python安装目录: { python_home } " )
69+
70+ # 标准库目录
71+ stdlib_dir = os .path .dirname (os .__file__ )
72+ print (f"标准库目录: { stdlib_dir } " )
73+
74+ # site-packages目录
75+ import site
76+ site_packages = site .getsitepackages ()
77+ print (f"site-packages目录: { site_packages } " )
78+
79+ # PYTHONPATH环境变量
80+ pythonpath = os .environ .get ('PYTHONPATH' , '' )
81+ if pythonpath :
82+ print (f"PYTHONPATH环境变量: { pythonpath } " )
83+ else :
84+ print ("PYTHONPATH环境变量: 未设置" )
85+
86+ # ============================================================================
87+ # 3. 演示模块搜索顺序
88+ # ============================================================================
89+
90+ def demonstrate_search_order ():
91+ """
92+ 演示模块搜索的优先级顺序
93+ """
94+ print ("\n === 3. 模块搜索顺序 ===" )
95+
96+ print ("Python按以下顺序搜索模块:" )
97+ print ("1. 内置模块 (built-in modules)" )
98+ print ("2. 当前目录或脚本所在目录" )
99+ print ("3. PYTHONPATH环境变量指定的目录" )
100+ print ("4. Python标准库目录" )
101+ print ("5. site-packages目录" )
102+ print ("6. .pth文件指定的目录" )
103+
104+ # 演示内置模块
105+ print ("\n 内置模块示例:" )
106+ builtin_modules = sys .builtin_module_names
107+ print (f"内置模块数量: { len (builtin_modules )} " )
108+ print (f"部分内置模块: { list (builtin_modules )[:10 ]} ..." )
109+
110+ # 检查特定模块的位置
111+ modules_to_check = ['os' , 'sys' , 'json' , 'datetime' ]
112+ print ("\n 标准库模块位置:" )
113+ for module_name in modules_to_check :
114+ try :
115+ module = __import__ (module_name )
116+ if hasattr (module , '__file__' ) and module .__file__ :
117+ print (f"{ module_name :10s} : { module .__file__ } " )
118+ else :
119+ print (f"{ module_name :10s} : [内置模块]" )
120+ except ImportError :
121+ print (f"{ module_name :10s} : [未找到]" )
122+
123+ # ============================================================================
124+ # 4. 动态修改搜索路径
125+ # ============================================================================
126+
127+ def demonstrate_path_modification ():
128+ """
129+ 演示如何动态修改模块搜索路径
130+ """
131+ print ("\n === 4. 动态修改搜索路径 ===" )
132+
133+ # 保存原始路径
134+ original_path = sys .path .copy ()
135+ print (f"原始路径数量: { len (original_path )} " )
136+
137+ # 添加新路径到开头(最高优先级)
138+ new_path = "/tmp/my_modules"
139+ sys .path .insert (0 , new_path )
140+ print (f"\n 在开头添加路径: { new_path } " )
141+ print (f"新的第一个搜索路径: { sys .path [0 ]} " )
142+
143+ # 添加路径到末尾(最低优先级)
144+ another_path = "/tmp/low_priority"
145+ sys .path .append (another_path )
146+ print (f"\n 在末尾添加路径: { another_path } " )
147+ print (f"当前路径数量: { len (sys .path )} " )
148+
149+ # 移除添加的路径
150+ if new_path in sys .path :
151+ sys .path .remove (new_path )
152+ print (f"\n 移除路径: { new_path } " )
153+
154+ if another_path in sys .path :
155+ sys .path .remove (another_path )
156+ print (f"移除路径: { another_path } " )
157+
158+ # 恢复原始路径
159+ sys .path = original_path
160+ print (f"\n 恢复原始路径,当前数量: { len (sys .path )} " )
161+
162+ # ============================================================================
163+ # 5. 创建和使用临时模块
164+ # ============================================================================
165+
166+ def create_temporary_module ():
167+ """
168+ 创建临时模块来演示搜索路径
169+ """
170+ print ("\n === 5. 创建临时模块演示 ===" )
171+
172+ # 创建临时目录
173+ temp_dir = "/tmp/python_module_demo"
174+ os .makedirs (temp_dir , exist_ok = True )
175+
176+ # 创建临时模块文件
177+ module_content = '''#!/usr/bin/env python3
178+ # -*- coding: utf-8 -*-
179+ """
180+ 临时演示模块
181+ """
182+
183+ TEMP_MODULE_NAME = "临时模块"
184+ TEMP_MODULE_VERSION = "1.0.0"
185+
186+ def temp_function():
187+ return "这是临时模块中的函数"
188+
189+ class TempClass:
190+ def __init__(self):
191+ self.name = "临时类"
192+
193+ def get_info(self):
194+ return f"我是{self.name}"
195+ '''
196+
197+ module_file = os .path .join (temp_dir , "temp_module.py" )
198+ with open (module_file , 'w' , encoding = 'utf-8' ) as f :
199+ f .write (module_content )
200+
201+ print (f"创建临时模块: { module_file } " )
202+
203+ # 将临时目录添加到搜索路径
204+ if temp_dir not in sys .path :
205+ sys .path .insert (0 , temp_dir )
206+ print (f"添加到搜索路径: { temp_dir } " )
207+
208+ # 尝试导入临时模块
209+ try :
210+ import temp_module
211+ print (f"\n 成功导入临时模块: { temp_module .TEMP_MODULE_NAME } " )
212+ print (f"模块版本: { temp_module .TEMP_MODULE_VERSION } " )
213+ print (f"调用函数: { temp_module .temp_function ()} " )
214+
215+ # 使用临时类
216+ temp_obj = temp_module .TempClass ()
217+ print (f"使用临时类: { temp_obj .get_info ()} " )
218+
219+ # 显示模块文件位置
220+ print (f"模块文件位置: { temp_module .__file__ } " )
221+
222+ except ImportError as e :
223+ print (f"导入临时模块失败: { e } " )
224+
225+ # 清理:移除搜索路径
226+ if temp_dir in sys .path :
227+ sys .path .remove (temp_dir )
228+ print (f"\n 从搜索路径移除: { temp_dir } " )
229+
230+ # 清理:删除临时文件
231+ try :
232+ os .remove (module_file )
233+ os .rmdir (temp_dir )
234+ print (f"清理临时文件: { module_file } " )
235+ except OSError :
236+ print (f"清理临时文件时出错" )
237+
238+ # ============================================================================
239+ # 6. 包的搜索机制
240+ # ============================================================================
241+
242+ def demonstrate_package_search ():
243+ """
244+ 演示包的搜索机制
245+ """
246+ print ("\n === 6. 包的搜索机制 ===" )
247+
248+ print ("包搜索规则:" )
249+ print ("1. 包必须是包含__init__.py文件的目录" )
250+ print ("2. 包的搜索遵循与模块相同的路径规则" )
251+ print ("3. 子包通过点号分隔的路径访问" )
252+ print ("4. 相对导入只能在包内部使用" )
253+
254+ # 检查一些常见包的位置
255+ packages_to_check = [
256+ 'json' ,
257+ 'urllib' ,
258+ 'xml' ,
259+ 'email' ,
260+ 'collections'
261+ ]
262+
263+ print ("\n 常见包的位置:" )
264+ for package_name in packages_to_check :
265+ try :
266+ package = __import__ (package_name )
267+ if hasattr (package , '__path__' ):
268+ print (f"{ package_name :12s} : { package .__path__ } " )
269+ elif hasattr (package , '__file__' ):
270+ print (f"{ package_name :12s} : { package .__file__ } " )
271+ else :
272+ print (f"{ package_name :12s} : [内置]" )
273+ except ImportError :
274+ print (f"{ package_name :12s} : [未找到]" )
275+
276+ # ============================================================================
277+ # 7. 搜索路径问题诊断
278+ # ============================================================================
279+
280+ def diagnose_import_issues ():
281+ """
282+ 诊断常见的导入问题
283+ """
284+ print ("\n === 7. 导入问题诊断 ===" )
285+
286+ print ("常见导入问题及解决方案:" )
287+ print ("\n 1. ModuleNotFoundError:" )
288+ print (" - 检查模块名拼写" )
289+ print (" - 确认模块在搜索路径中" )
290+ print (" - 检查PYTHONPATH设置" )
291+
292+ print ("\n 2. 相对导入问题:" )
293+ print (" - 相对导入只能在包内使用" )
294+ print (" - 使用绝对导入更安全" )
295+
296+ print ("\n 3. 循环导入:" )
297+ print (" - 重新设计模块结构" )
298+ print (" - 使用延迟导入" )
299+
300+ print ("\n 4. 路径问题:" )
301+ print (" - 检查当前工作目录" )
302+ print (" - 使用绝对路径" )
303+ print (" - 正确设置PYTHONPATH" )
304+
305+ # 提供诊断工具
306+ print ("\n 诊断工具函数:" )
307+
308+ def find_module_path (module_name ):
309+ """查找模块的实际路径"""
310+ try :
311+ import importlib .util
312+ spec = importlib .util .find_spec (module_name )
313+ if spec :
314+ return spec .origin or spec .submodule_search_locations
315+ else :
316+ return None
317+ except ImportError :
318+ return None
319+
320+ # 测试诊断工具
321+ test_modules = ['os' , 'sys' , 'nonexistent_module' ]
322+ for module in test_modules :
323+ path = find_module_path (module )
324+ if path :
325+ print (f" { module } : { path } " )
326+ else :
327+ print (f" { module } : [未找到]" )
328+
329+ # ============================================================================
330+ # 8. 最佳实践建议
331+ # ============================================================================
332+
333+ def show_best_practices ():
334+ """
335+ 显示模块搜索路径的最佳实践
336+ """
337+ print ("\n === 8. 最佳实践建议 ===" )
338+
339+ practices = [
340+ "1. 使用虚拟环境管理项目依赖" ,
341+ "2. 避免修改sys.path,除非绝对必要" ,
342+ "3. 使用相对于项目根目录的导入" ,
343+ "4. 在setup.py或pyproject.toml中声明依赖" ,
344+ "5. 使用绝对导入而不是相对导入" ,
345+ "6. 保持模块结构简单清晰" ,
346+ "7. 使用包来组织相关模块" ,
347+ "8. 避免在运行时动态修改搜索路径" ,
348+ "9. 使用importlib进行动态导入" ,
349+ "10. 定期清理不需要的模块和包"
350+ ]
351+
352+ for practice in practices :
353+ print (f" { practice } " )
354+
355+ print ("\n 推荐的项目结构:" )
356+ print ("""
357+ my_project/
358+ ├── setup.py
359+ ├── requirements.txt
360+ ├── my_package/
361+ │ ├── __init__.py
362+ │ ├── module1.py
363+ │ └── subpackage/
364+ │ ├── __init__.py
365+ │ └── module2.py
366+ ├── tests/
367+ │ └── test_module1.py
368+ └── docs/
369+ └── README.md
370+ """ )
371+
372+ # ============================================================================
373+ # 主函数
374+ # ============================================================================
375+
376+ def main ():
377+ """
378+ 主函数:演示所有模块搜索路径相关功能
379+ """
380+ print ("Python模块搜索路径演示" )
381+ print ("=" * 60 )
382+
383+ show_current_search_paths ()
384+ analyze_path_sources ()
385+ demonstrate_search_order ()
386+ demonstrate_path_modification ()
387+ create_temporary_module ()
388+ demonstrate_package_search ()
389+ diagnose_import_issues ()
390+ show_best_practices ()
391+
392+ print ("\n " + "=" * 60 )
393+ print ("模块搜索路径演示完成!" )
394+ print ("=" * 60 )
395+
396+ if __name__ == "__main__" :
397+ main ()
0 commit comments