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 logging模块详解及其日志定时清理方式

《pythonlogging模块详解及其日志定时清理方式》:本文主要介绍pythonlogging模块详解及其日志定时清理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录python logging模块及日志定时清理1.创建logger对象2.logging.basicCo

Python如何自动生成环境依赖包requirements

《Python如何自动生成环境依赖包requirements》:本文主要介绍Python如何自动生成环境依赖包requirements问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录生成当前 python 环境 安装的所有依赖包1、命令2、常见问题只生成当前 项目 的所有依赖包1、

如何将Python彻底卸载的三种方法

《如何将Python彻底卸载的三种方法》通常我们在一些软件的使用上有碰壁,第一反应就是卸载重装,所以有小伙伴就问我Python怎么卸载才能彻底卸载干净,今天这篇文章,小编就来教大家如何彻底卸载Pyth... 目录软件卸载①方法:②方法:③方法:清理相关文件夹软件卸载①方法:首先,在安装python时,下

python uv包管理小结

《pythonuv包管理小结》uv是一个高性能的Python包管理工具,它不仅能够高效地处理包管理和依赖解析,还提供了对Python版本管理的支持,本文主要介绍了pythonuv包管理小结,具有一... 目录安装 uv使用 uv 管理 python 版本安装指定版本的 Python查看已安装的 Python

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

Python中局部变量和全局变量举例详解

《Python中局部变量和全局变量举例详解》:本文主要介绍如何通过一个简单的Python代码示例来解释命名空间和作用域的概念,它详细说明了内置名称、全局名称、局部名称以及它们之间的查找顺序,文中通... 目录引入例子拆解源码运行结果如下图代码解析 python3命名空间和作用域命名空间命名空间查找顺序命名空

Python如何将大TXT文件分割成4KB小文件

《Python如何将大TXT文件分割成4KB小文件》处理大文本文件是程序员经常遇到的挑战,特别是当我们需要把一个几百MB甚至几个GB的TXT文件分割成小块时,下面我们来聊聊如何用Python自动完成这... 目录为什么需要分割TXT文件基础版:按行分割进阶版:精确控制文件大小完美解决方案:支持UTF-8编码

基于Python打造一个全能文本处理工具

《基于Python打造一个全能文本处理工具》:本文主要介绍一个基于Python+Tkinter开发的全功能本地化文本处理工具,它不仅具备基础的格式转换功能,更集成了中文特色处理等实用功能,有需要的... 目录1. 概述:当文本处理遇上python图形界面2. 功能全景图:六大核心模块解析3.运行效果4. 相

Python中的魔术方法__new__详解

《Python中的魔术方法__new__详解》:本文主要介绍Python中的魔术方法__new__的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、核心意义与机制1.1 构造过程原理1.2 与 __init__ 对比二、核心功能解析2.1 核心能力2.2

Python虚拟环境终极(含PyCharm的使用教程)

《Python虚拟环境终极(含PyCharm的使用教程)》:本文主要介绍Python虚拟环境终极(含PyCharm的使用教程),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录一、为什么需要虚拟环境?二、虚拟环境创建方式对比三、命令行创建虚拟环境(venv)3.1 基础命令3