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

相关文章

Windows系统宽带限制如何解除?

《Windows系统宽带限制如何解除?》有不少用户反映电脑网速慢得情况,可能是宽带速度被限制的原因,只需解除限制即可,具体该如何操作呢?本文就跟大家一起来看看Windows系统解除网络限制的操作方法吧... 有不少用户反映电脑网速慢得情况,可能是宽带速度被限制的原因,只需解除限制即可,具体该如何操作呢?本文

Python中模块graphviz使用入门

《Python中模块graphviz使用入门》graphviz是一个用于创建和操作图形的Python库,本文主要介绍了Python中模块graphviz使用入门,具有一定的参考价值,感兴趣的可以了解一... 目录1.安装2. 基本用法2.1 输出图像格式2.2 图像style设置2.3 属性2.4 子图和聚

CentOS和Ubuntu系统使用shell脚本创建用户和设置密码

《CentOS和Ubuntu系统使用shell脚本创建用户和设置密码》在Linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设置密码,本文写了一个shell... 在linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设

Python使用Matplotlib绘制3D曲面图详解

《Python使用Matplotlib绘制3D曲面图详解》:本文主要介绍Python使用Matplotlib绘制3D曲面图,在Python中,使用Matplotlib库绘制3D曲面图可以通过mpl... 目录准备工作绘制简单的 3D 曲面图绘制 3D 曲面图添加线框和透明度控制图形视角Matplotlib

电脑找不到mfc90u.dll文件怎么办? 系统报错mfc90u.dll丢失修复的5种方案

《电脑找不到mfc90u.dll文件怎么办?系统报错mfc90u.dll丢失修复的5种方案》在我们日常使用电脑的过程中,可能会遇到一些软件或系统错误,其中之一就是mfc90u.dll丢失,那么,mf... 在大部分情况下出现我们运行或安装软件,游戏出现提示丢失某些DLL文件或OCX文件的原因可能是原始安装包

电脑显示mfc100u.dll丢失怎么办?系统报错mfc90u.dll丢失5种修复方案

《电脑显示mfc100u.dll丢失怎么办?系统报错mfc90u.dll丢失5种修复方案》最近有不少兄弟反映,电脑突然弹出“mfc100u.dll已加载,但找不到入口点”的错误提示,导致一些程序无法正... 在计算机使用过程中,我们经常会遇到一些错误提示,其中最常见的就是“找不到指定的模块”或“缺少某个DL

一文教你Python如何快速精准抓取网页数据

《一文教你Python如何快速精准抓取网页数据》这篇文章主要为大家详细介绍了如何利用Python实现快速精准抓取网页数据,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录1. 准备工作2. 基础爬虫实现3. 高级功能扩展3.1 抓取文章详情3.2 保存数据到文件4. 完整示例

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

基于Python打造一个智能单词管理神器

《基于Python打造一个智能单词管理神器》这篇文章主要为大家详细介绍了如何使用Python打造一个智能单词管理神器,从查询到导出的一站式解决,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 项目概述:为什么需要这个工具2. 环境搭建与快速入门2.1 环境要求2.2 首次运行配置3. 核心功能使用指

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获