Python 包重名, 导入系统包

2024-01-13 20:28
文章标签 python 系统 导入 重名

本文主要是介绍Python 包重名, 导入系统包,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

来源

之前使用PyQt5写的程序,但是目标系统没有,但是有PySide2/PySide6,因其授权更宽松。因为程序比较简单,那么,能不能一劳永逸?兼容各种Qt库版本呢?

思路

写一个自己的PyQt5包,处理兼容问题。这也就引出了当前的问题,在一个包里面,导入同名的系统包问题,或者叫“包重载”。

解决方案

这个问题耗了我2天时间

最终的解决方案是,使用修改包导入函数。参考官方importlib的导入包示例代码,但导入包时需要加前缀,并处理导入列表。

import os
import sys
import importlib.util__this_package_path__ = os.path.abspath(__package__ + '/..')__sys_path_without_this__ = [p for p in sys.path if not p.startswith(__this_package_path__)]def import_from_path(name,package=None,path=None,globals=None,locals=None,fromlist=(),rename=None
):"""An approximate implementation of import."""absolute_name = importlib.util.resolve_name(name, package)if rename:sys_module_name = '{}{}'.format(rename, absolute_name)else:sys_module_name = absolute_nametry:return sys.modules[sys_module_name]except KeyError:passhas_parent = Falseif '.' in absolute_name:has_parent = Trueparent_name, _, child_name = absolute_name.rpartition('.')parent_module = import_from_path(parent_name,package=package,path=path,globals=globals,locals=locals,fromlist=(),rename=rename,)path = parent_module.__spec__.submodule_search_locationsfor finder in sys.meta_path:spec = finder.find_spec(absolute_name, path)if spec is not None:breakelse:msg = f'No module named {absolute_name!r}'raise ModuleNotFoundError(msg, name=absolute_name)module = importlib.util.module_from_spec(spec)sys.modules[sys_module_name] = modulespec.loader.exec_module(module)if has_parent:setattr(parent_module, child_name, module)if len(fromlist) > 0:if '*' in fromlist:# is there an __all__?  if so respect itif "__all__" in module.__dict__:names = module.__dict__["__all__"]else:# otherwise we import all names that don't begin with _names = [x for x in module.__dict__ if not x.startswith("_")]else:names = fromlist# now drag them inif locals:locals.update({k: getattr(module, k) for k in names})if globals:globals.update({k: getattr(module, k) for k in names})return moduledef import_sys_module(name, package=None, globals=None, locals=None, fromlist=()):return import_from_path(name,package,__sys_path_without_this__,globals=globals,locals=locals,fromlist=fromlist,rename='Sys')

之后对于同名包,导入时使用import_sys_module函数导入

# PyQt5/__init__.py__qt_module__ = Nonetry:from PySide2 import *__qt_module__ = "PySide2"
except ImportError:try:from PySide6 import *__qt_module__ = "PySide6"except ImportError:try:import_sys_module('PyQt5',globals=globals(),locals=locals(),fromlist=('*',))__qt_module__ = "PyQt5"except ImportError:try:from PyQt6 import *__qt_module__ = "PyQt6"except ImportError:raise

其他模块

# PyQt5/QtCore/__init__.pyfrom .. import __qt_module__, import_sys_module
if __qt_module__ == 'PySide2':from PySide2.QtCore import *
elif __qt_module__ == 'PySide6':from PySide6.QtCore import *from PySide6.QtCore import Signal as pyqtSignal
elif __qt_module__ == 'PyQt5':import_sys_module('PyQt5.QtCore', globals=globals(), locals=locals(), fromlist=('*',))
elif __qt_module__ == 'PyQt6':from PyQt6.QtCore import *
else:raise ValueError('Can not handle this qt module: ', __qt_module__)

模块好多啊, 那么能不能写个代码生成模块呢?

import os
import syscompat_qt_module_name = 'PyQt5'submodules = ['QtCore', 'QtGui', 'QtWidgets', 'sip']compat_qt_source_modules = ['PySide2', 'PySide6', 'PyQt5', 'PyQt6']# generate main module __init__.py codes
main_module_init_string = '''
import os
import sys
import importlib.util__this_package_path__ = os.path.abspath(__package__ + '/..')__sys_path_without_this__ = [p for p in sys.path if not p.startswith(__this_package_path__)]def import_from_path(name,package=None,path=None,globals=None,locals=None,fromlist=(),rename=None
):"""An approximate implementation of import."""absolute_name = importlib.util.resolve_name(name, package)if rename:sys_module_name = '{}{}'.format(rename, absolute_name)else:sys_module_name = absolute_nametry:return sys.modules[sys_module_name]except KeyError:passhas_parent = Falseif '.' in absolute_name:has_parent = Trueparent_name, _, child_name = absolute_name.rpartition('.')parent_module = import_from_path(parent_name,package=package,path=path,globals=globals,locals=locals,fromlist=(),rename=rename,)path = parent_module.__spec__.submodule_search_locationsfor finder in sys.meta_path:spec = finder.find_spec(absolute_name, path)if spec is not None:breakelse:msg = f'No module named {absolute_name!r}'raise ModuleNotFoundError(msg, name=absolute_name)module = importlib.util.module_from_spec(spec)sys.modules[sys_module_name] = modulespec.loader.exec_module(module)if has_parent:setattr(parent_module, child_name, module)if len(fromlist) > 0:if '*' in fromlist:# is there an __all__?  if so respect itif "__all__" in module.__dict__:names = module.__dict__["__all__"]else:# otherwise we import all names that don't begin with _names = [x for x in module.__dict__ if not x.startswith("_")]else:names = fromlist# now drag them inif locals:locals.update({k: getattr(module, k) for k in names})if globals:globals.update({k: getattr(module, k) for k in names})return moduledef import_sys_module(name, package=None, globals=None, locals=None, fromlist=()):return import_from_path(name,package,__sys_path_without_this__,globals=globals,locals=locals,fromlist=fromlist,rename='Sys')
'''main_module_init_string += '__qt_module__ = None\n'for i, sm in enumerate(compat_qt_source_modules):main_module_init_string += '    ' * i + 'try:\n'if not sm.startswith('PyQt5'):main_module_init_string += '    ' * i + '    from {} import *\n'.format(sm)else:main_module_init_string += '    ' * i + '    import_sys_module(\n'main_module_init_string += '    ' * i + '        \'PyQt5\',\n'main_module_init_string += '    ' * i + '        globals=globals(),\n'main_module_init_string += '    ' * i + '        locals=locals(),\n'main_module_init_string += '    ' * i + '        fromlist=(\'*\',)\n'main_module_init_string += '    ' * i + '    )\n'main_module_init_string += '    ' * i + '    __qt_module__ = "{}"\n'.format(sm)main_module_init_string += '    ' * i + 'except ImportError:\n'if i != len(compat_qt_source_modules) - 1:continueelse:main_module_init_string += '    ' * i + '    raise\n'print(main_module_init_string)
main_compat_qt_module_file = '{}/__init__.py'.format(compat_qt_module_name)os.makedirs(os.path.dirname(main_compat_qt_module_file), exist_ok=True)
with open(main_compat_qt_module_file, 'w') as fp:fp.write(main_module_init_string)# generate submodule __init__.py codes
for i, sm in enumerate(submodules):submodule_init_string = '\n'submodule_init_string += 'from {} import __qt_module__, import_sys_module\n'.format('.' * (len(sm.split('/')) + 1))for j, srm in enumerate(compat_qt_source_modules):submodule_init_string += '{} __qt_module__ == \'{}\':\n'.format('if' if j == 0 else 'elif', srm)if srm.startswith('PyQt5'):submodule_init_string += '    import_sys_module(\'{}.{}\',' \' globals=globals(), locals=locals(), fromlist=(' \'\'*\',))\n'.format(srm, sm)else:submodule_init_string += '    from {}.{} import *\n'.format(srm, sm)submodule_init_string += 'else:\n'submodule_init_string += '    raise ValueError(\'Can not handle this qt module: \', __qt_module__)\n'print(submodule_init_string)compat_qt_submodule_file = '{}/{}/__init__.py'.format(compat_qt_module_name, sm)os.makedirs(os.path.dirname(compat_qt_submodule_file), exist_ok=True)with open(compat_qt_submodule_file, 'w') as fp:fp.write(submodule_init_string)if __name__ == '__main__':pass

最后

本文并没有处理所有的兼容问题,如pyqtSignal 在 PySide6中没有等,程序中用的其他涉及兼容的问题还需要进一步处理。

对Qt和Python了解都不够深入,有不足之处,还请多多指正!

这篇关于Python 包重名, 导入系统包的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/602680

相关文章

python中列表list切分的实现

《python中列表list切分的实现》列表是Python中最常用的数据结构之一,经常需要对列表进行切分操作,本文主要介绍了python中列表list切分的实现,文中通过示例代码介绍的非常详细,对大家... 目录一、列表切片的基本用法1.1 基本切片操作1.2 切片的负索引1.3 切片的省略二、列表切分的高

基于Python实现一个PDF特殊字体提取工具

《基于Python实现一个PDF特殊字体提取工具》在PDF文档处理场景中,我们常常需要针对特定格式的文本内容进行提取分析,本文介绍的PDF特殊字体提取器是一款基于Python开发的桌面应用程序感兴趣的... 目录一、应用背景与功能概述二、技术架构与核心组件2.1 技术选型2.2 系统架构三、核心功能实现解析

通过Python脚本批量复制并规范命名视频文件

《通过Python脚本批量复制并规范命名视频文件》本文介绍了如何通过Python脚本批量复制并规范命名视频文件,实现自动补齐数字编号、保留原始文件、智能识别有效文件等功能,听过代码示例介绍的非常详细,... 目录一、问题场景:杂乱的视频文件名二、完整解决方案三、关键技术解析1. 智能路径处理2. 精准文件名

基于Python开发PDF转Doc格式小程序

《基于Python开发PDF转Doc格式小程序》这篇文章主要为大家详细介绍了如何基于Python开发PDF转Doc格式小程序,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用python实现PDF转Doc格式小程序以下是一个使用Python实现PDF转DOC格式的GUI程序,采用T

Python使用PIL库将PNG图片转换为ICO图标的示例代码

《Python使用PIL库将PNG图片转换为ICO图标的示例代码》在软件开发和网站设计中,ICO图标是一种常用的图像格式,特别适用于应用程序图标、网页收藏夹图标等场景,本文将介绍如何使用Python的... 目录引言准备工作代码解析实践操作结果展示结语引言在软件开发和网站设计中,ICO图标是一种常用的图像

使用Python开发一个图像标注与OCR识别工具

《使用Python开发一个图像标注与OCR识别工具》:本文主要介绍一个使用Python开发的工具,允许用户在图像上进行矩形标注,使用OCR对标注区域进行文本识别,并将结果保存为Excel文件,感兴... 目录项目简介1. 图像加载与显示2. 矩形标注3. OCR识别4. 标注的保存与加载5. 裁剪与重置图像

使用Python实现表格字段智能去重

《使用Python实现表格字段智能去重》在数据分析和处理过程中,数据清洗是一个至关重要的步骤,其中字段去重是一个常见且关键的任务,下面我们看看如何使用Python进行表格字段智能去重吧... 目录一、引言二、数据重复问题的常见场景与影响三、python在数据清洗中的优势四、基于Python的表格字段智能去重

Python中如何控制小数点精度与对齐方式

《Python中如何控制小数点精度与对齐方式》在Python编程中,数据输出格式化是一个常见的需求,尤其是在涉及到小数点精度和对齐方式时,下面小编就来为大家介绍一下如何在Python中实现这些功能吧... 目录一、控制小数点精度1. 使用 round() 函数2. 使用字符串格式化二、控制对齐方式1. 使用

Python如何快速下载依赖

《Python如何快速下载依赖》本文介绍了四种在Python中快速下载依赖的方法,包括使用国内镜像源、开启pip并发下载功能、使用pipreqs批量下载项目依赖以及使用conda管理依赖,通过这些方法... 目录python快速下载依赖1. 使用国内镜像源临时使用镜像源永久配置镜像源2. 使用 pip 的并

Nginx配置系统服务&设置环境变量方式

《Nginx配置系统服务&设置环境变量方式》本文介绍了如何将Nginx配置为系统服务并设置环境变量,以便更方便地对Nginx进行操作,通过配置系统服务,可以使用系统命令来启动、停止或重新加载Nginx... 目录1.Nginx操作问题2.配置系统服android务3.设置环境变量总结1.Nginx操作问题