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脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Python将大量遥感数据的值缩放指定倍数的方法(推荐)

《Python将大量遥感数据的值缩放指定倍数的方法(推荐)》本文介绍基于Python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处理,并将所得处理后数据保存为新的遥感影像... 本文介绍基于python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处

python管理工具之conda安装部署及使用详解

《python管理工具之conda安装部署及使用详解》这篇文章详细介绍了如何安装和使用conda来管理Python环境,它涵盖了从安装部署、镜像源配置到具体的conda使用方法,包括创建、激活、安装包... 目录pytpshheraerUhon管理工具:conda部署+使用一、安装部署1、 下载2、 安装3

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

在C#中获取端口号与系统信息的高效实践

《在C#中获取端口号与系统信息的高效实践》在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C#作为一种广泛应用的编程语言,提供了丰富的API来帮助开... 目录引言1. 获取端口号信息1.1 获取活动的 TCP 和 UDP 连接说明:应用场景:2. 获取硬

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

《JAVA系统中SpringBoot应用程序的配置文件application.yml使用详解》:本文主要介绍JAVA系统中SpringBoot应用程序的配置文件application.yml的... 目录文件路径文件内容解释1. Server 配置2. Spring 配置3. Logging 配置4. Ma

2.1/5.1和7.1声道系统有什么区别? 音频声道的专业知识科普

《2.1/5.1和7.1声道系统有什么区别?音频声道的专业知识科普》当设置环绕声系统时,会遇到2.1、5.1、7.1、7.1.2、9.1等数字,当一遍又一遍地看到它们时,可能想知道它们是什... 想要把智能电视自带的音响升级成专业级的家庭影院系统吗?那么你将面临一个重要的选择——使用 2.1、5.1 还是

Python MySQL如何通过Binlog获取变更记录恢复数据

《PythonMySQL如何通过Binlog获取变更记录恢复数据》本文介绍了如何使用Python和pymysqlreplication库通过MySQL的二进制日志(Binlog)获取数据库的变更记录... 目录python mysql通过Binlog获取变更记录恢复数据1.安装pymysqlreplicat

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的