当前位置: 首页 > 后端技术 > Python

【Python】解决python3中import的疑难杂症

时间:2023-03-26 00:13:34 Python

python中的import和包管理概念:模块和包模块模块:一般为.py后缀的文件,包括.pyo、.pyc、.pyd、带有.so和.dll后缀的文件。函数、类和变量在模块中定义。在项目中,如果某个文件夹下有__init__.py文件,则该文件夹会被认为是一个包,这样可以方便项目文件的组织,避免模块名冲突。当__init__.py为空时,仅用于标识当前文件夹为包。package__all__变量指示在导入包时将导入哪些模块*。您可以使用__init__.py向外界提供类型、变量和接口。用户隐藏了每个子模块的实现细节当我们导入一个包时,会自动加载该包对应的__init__.py,所以如果在里面做过于复杂的计算,会造成不必要的开销。sys.modulesssys.modules维护了一个已加载模块的字典,第二次加载模块时可以直接从字典中查找,以加快执行速度。importsysprint(sys.modules)//输出:{'random':,'subprocess':,'sysconfig':,'gc':}namespacelocalnamespace:函数的命名空间,记录函数变量globalnamespace:模块的命名空间,记录模块的变量(函数、类、引入的模块、模块级变量和常量)build-innamespace:包含内置函数和异常,可以是任何模块访问import方法影响我们使用包的方式,这就是命名空间作用的体现:fromfooimportbar#将模块foo中的函数/变量bar导入到当前modu的命名空间中le,可以直接访问barimportfoo#importmodulefoowhileretainingitselfbar模块的命名空间需要通过foo.bar来访问。__doc__:文件注释__file__:当前文件路径__package__:导入文件路径__cached__:导入文件缓存路径__name__:导入文件路径添加文件名__builtins__:包含内置函数Python内置模块os:提供系统文件、目录等级别的操作sys:提供解释器相关操作hashlib:提供加密相关操作,替代md5和sha模块shutil:提供文件、文件夹、压缩包等处理模块configparser:提供对具体配置的操作kv函数通过文件持久化内存数据导入方式一、介绍Python中import的常用操作是:importsomemodule#importentiremodulefromsomemoduleimportsomefunction#importfromsomemodulemodule中的单个函数importfirstfunc,secondfunc,thirdfunc#importmultiplefunctionsfromamodulefromsomemoduleimport*#importallfunctionsfromamodule2.执行import步骤创建一个新的模块对象并将模块对象插入到sys.modules中加载模块代码执行新的对应moduleCode3.importsearchpackageorder注意加载模块代码的时候in第三步,python解释器需要先搜索对应的.py文件,搜索顺序为:sys.path:包含当前脚本路径和其他搜索包(系统库、第三方库等).),也可以在代码中通过sys.path.append()动态添加搜索路径PYTHONPATH查看默认路径,如Linux下的/usr/local/lib/python/4。绝对导入和相对导入绝对导入和相对导入的概念只是针对包中的模块导入包中的模块。注意,如果foo.py和bar.py在同一个非包(没有__init__.py文件)目录下,那么它们之间是可以互相导入的,不存在绝对导入和相对导入的问题。Python3推荐使用绝对导入。例如:$treemypackage├──__init__.py├──module_bar.py└──module_foo.py在包mypackage中,如果module_bar要导入module_foo,有以下三种方式:#方法一:importmodule_foo#方法二:#如果是上层文件夹write..,则上层文件夹写...,以此类推。importmodule_foo#方法三:frommypackageimportmodule_fooimportmypackage.module_foo对于python2,方法一和方法二是相对导入,效果是一样的,只是前者叫隐式相对导入,后者叫显式相对导入,方法三对于python3是绝对导入(会在sys.path中的路径中查找),方法二是相对导入,方法一和方法三是绝对导入,推荐使用官方的方法三。5.包导入包的导入与模块的导入基本相同,只是在导入包时会执行__init__.py。如果只是导入一个packageimportpackage,没有命名任何模块,并且package中的__init__.py没有其他初始化操作,那么package下的modules是无法自动导入的。6.直接运行和模块运行下面以工程为例:$tree.└──mypackage├──__init__.py└──module_foo.py#module_foo.py内容如下:importsysprint(sys.path)我们有两种Runmodule_foo.pyinoneway:-m参数表示runlibrarymoduleasascript,即把模块作为脚本来执行。#直接运行:第一个目录为module_foo所在$python3-Bmypackage/module_foo.py['/Users/didi/Desktop/MyProject/mypackage','/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip','/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7','/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload','/Users/didi/Library/Python/3.7/lib/python/site-packages','/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages']#模块运行:第一个目录为当前路径$python3-B-mmypackage.module_foo['/Users/didi/Desktop/MyProject','/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip','/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7','/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload','/Users/didi/Library/Python/3.7/lib/python/site-packages','/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages']示例:包间问题模块引用1.项目demo假设你当前的项目文件目录如下(仅针对python3):注意我的文件夹里没有__init__.py。严格来说,它们不是包裹。他们只是将密切相关的模块放在同一个文件夹中,以方便项目管理.└──src├──bar_package│└──module_1.py├──foo_package│├──module_2.py│└──module_3.py└──main.py#Note#1)所有模块都以src开头对于根目录,包括main.py(当然这只是我个人的习惯)#2)导入方式为绝对导入(推荐使用python3)"""module_1.py:空文件""""""module_2.py:importmodule_3inthesamepackage"""fromfoo_packageimportmodule_3#Refertomodulesinthesamepackage"""module_3.py:importmodule_1inanotherpackage"""frombar_packageimportmodule_1#跨包引用moduleif__name__=="__main__":print("module_3execsuccessful!")""main.py:importallmodules"""fromfoo_packageimportmodule_3,module_2frombar_packageimportmodule_1以上是项目文件包管理的常用方式,执行整个程序:python3-Bsrc/main.py2。问题:单独执行一个模块如果想单独执行module_3.py,此时会报错:$python3-Bsrc/foo_package/module_3.pyTraceback(mostrecentcalllast):File"src/foo_package/module_3.py",第1行,在frombar_packageimportmodulee_1#跨包引用模块ModuleNotFoundError:Nomodulenamed'bar_package'回头看前面提到的导入搜索包的路径,我们有两种方法可以解决这个问题3、方法一:通过运行模块解决(推荐)本质上我们是想把module_3.py模块作为脚本运行,所以可以带上-m参数:$cdsrc#代码以src为根目录是的,所以需要去src$python3-B-mfoo_package.module_3module_3exec成功!4、方法二:在sys.path中加入搜索路径报之前的错误是找不到bar_package的模块名,因为如果直接运行sys.path的第一个路径就是module_3.py的路径。自然找不到上层的bar_package。我们也可以通过sys.path.append(..)将其上层目录添加到sys.path中,修改后的module_3.py文件内容为:"""module_3.py本质上是添加了上层目录module_3.py到sys.path,以便找到bar_package。"""importosimportsysparent_path=os.path.dirname(sys.path[0])ifparent_pathnotinsys.path:sys.path.append(parent_path)frombar_packageimportmodule_1#跨包引用模块if__name__=="__main__":print("module_3execsuccessfully!")注意事项是的,如果使用下面的写法,可能还是会出现问题:"""module_3.py"""importsyssys.path.append("../")frombar_packageimportmodule_1#跨包引用模块if__name__=="__main__":print("module_3execsuccessfully!")#enterwheremodule_3.py位于目录,输出正常:$src/foo_package$python3-Bmodule_3.pymodule_3execsuccessful!#直接在根目录下执行会报错:$python3-Bsrc/foo_package/module_3.pyTraceback(最近调用last):File"src/foo_package/module_3.py",line3,infrombar_packageimportmodule_1#Cross-packagereferencemoduleModuleNotFoundError:Nomodulenamed'bar_package'另一种简洁的写法是:importsysimportosssys。path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))5.尽量不要使用相对引用Python3不推荐使用相对引用,最好遵循某些开发规范,不在代码中混合绝对引用和相对引用参考[1]https://blog.csdn.net/weixin_...[2]https://zhuanlan.zhihu.com/p/...[3]https://www.cnblogs.com/schip...[4]https://www.jianshu.com/p/88b...