01 | 问题描述
最近有一个需求,需要自动地导入某一个文件夹的.py文件,例如:
--app
----model
------__init__.py
------a.py
------b.py
--__init__.py
具体的需求是:在app的__init__.py文件中批量导入model文件夹中的.py文件,之前的实现方式(主要是借助__import__)是:
model/__init__.py
import os
import pkgutil
pkg_path, _ = os.path.split(os.path.relpath(__file__))
for _, file, _ in pkgutil.iter_modules([pkg_path]):
__import__(pkg_path.replace("\", ".") + "." + file)
app/__init__.py
from app.model import *
在Windows开发环境中没有一切正常,但是运行在Centos服务器中报错:No module named 'app/model'。
02 | 问题定位
在model/__init__.py文件的第7行之前添加2行内容,分别查看pkg_path和file的内容:
model/__init__.py
import os
import pkgutil
pkg_path, _ = os.path.split(os.path.relpath(__file__))
for _, file, _ in pkgutil.iter_modules([pkg_path]):
print(pkg_path)
print(file)
__import__(pkg_path.replace("\", ".") + "." + file)
在Windows操作系统中pkg_path打印值为:app\model,Centos操作系统的打印值为:app/model,也就说,之前的pkg_path.replace("\", ".")在Centos操作系统中没有起作用。
03 | 解决方法
03-1 | 根据当前运行的平台确定替换内容
简单的来讲,首先通过Python语句判断当前系统运行的平台,是Windows还是Linux系统,然后根据平台确定要替换的内容,Windows就替换``,Linux就替换/,具体代码如下:
import os
import pkgutil
import platform
pkg_path, _ = os.path.split(os.path.relpath(__file__))
platform_system = platform.system().lower
for _, file, _ in pkgutil.iter_modules([pkg_path]):
if platform_system == "windows"
__import__(pkg_path.replace("\", ".") + "." + file)
elif platform_system == "linux":
__import__(pkg_path.replace("/", ".") + "." + file)
但这种方式总感觉不是很优雅,每次遍历总要做if-else判断。
03-2 | 统一pkg_path的格式
无论在哪种操作系统下,统一pkg_path的值(也就是绝对路径)为一个格式,例如:Linux系统的格式。大概有两种方式,一种是获得pkg_path的值以后,调用replace方法,将Windows系统中的``替换为/;另一种是调用pathlib.PurePath.as_posix()方法,这个是Python 3.4新增的方法。第一种方法比较简单,代码参考__import__语句中的方法即可。这里列出第二种方法的代码:
# !/venv/Scripts/python
# -*- coding: utf-8 -*-
# @Time : 2022/02/10
# @Author : Jiao Xiang Ning
# @Email : jiaoxn@geoscene.cn
# @File : __init__.py.py
import os
import pkgutil
from pathlib import Path
pkg_path, _ = os.path.split(os.path.relpath(__file__))
pkg_path = Path(pkg_path).as_posix() # 统一路径格式为Unix格式
for _, file, _ in pkgutil.iter_modules([pkg_path]):
__import__(pkg_path.replace("/", ".") + "." + file)
04 | 技术探秘
pathlib是从Python 3.4开始引入的,在Python 3.6已经基本成熟了。pathlib是面向对象的文件系统路径工具,提供了表示文件系统的类,这些类具有适用于不同操作系统的语义,官网地址。
pathlib中的路径类分为2种:纯路径和具体路径,纯路径(PurePath)没有I/O的计算操作,具体路径从PurePath中继承并提供了I/O操作。如果之前没用过pathlib或者不知道用哪个类,大部分情况下可直接使用Path类。
pathlib相对于之前的os.path有这个几个优势:
- 之前的
os.path操作函数管理比较混乱,有的是导入os,有的是导入os.path;pathlib只需要导入pathlib os.path在多个操作系统间切换比较麻烦,通常还会需要修改部分代码;pathlib很方便
os.path返回值通常是字符串,但是路径和字符串并非完全等价,之前要操作路径时可能需要额外的操作;pathlib模块是面向对象,处理更灵活pathlib简化了很多操作
来几个示例对比一下吧:
- 获取当前路径:
os.path
import os
print(os.getcwd()) # C:\Users\jiaoxn\Desktop\example
pathlib
import pathlib
print(pathlib.Path.cwd())
- 拼接路径
os.path
import os
print(os.path.join("a", "b"))
pathlib
import pathlib
paths = ["a", "b"]
print(pathlib.Path().parent.joinpath(paths))
\