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

Python中import的用法详解

时间:2023-03-26 00:06:35 Python

Python用了快两年了,有些东西用得发懵,而import是一直没搞懂的东西。曾经有过三次解决的机会,但因蒙混过关、拖沓拖延,没能化敌为友。今天下午,它又给了我一次机会,我想我还是得偿所愿。故事从这位台湾同胞的博客《Python的import陷阱》[1](见底部网址)开始,然后跳转到Python社区的PEP328提案[2],结合以往的经验和一些测试,我想我大概明白了.以下是我的总结,希望内容能够简明扼要,通俗易懂。导入语句有什么用?import语句用于导入其他python文件(称为模块模块),使用模块中定义的类、方法或变量来达到代码重用的目的。为了方便讲解,我们用例子来说明import的用法,读者朋友们可以跟着试一试(尝试时推荐使用python3,python2和python3在import上有不同的表现,后面会提到)。首先,创建一个文件夹Tree作为工作目录,并在其中创建两个文件m1.py和m2.py,并在m1.py中写入代码:importosimportm2m2.printSelf()在m2.py中写入代码:defprintSelf():print('Inm2')打开命令行,进入Tree目录,输入pythonm1.py运行,发现没有报错,并且打印出Inm2,说明没有问题以这种方式使用导入。由此我们总结出import语句的第一个用法:importmodule_name。即导入后直接连接模块名。在这种情况下,Python会在两个地方寻找这个模块,第一个是sys.path(通过运行代码importsys;print(sys.path)查看),os模块所在的目录在列表中sys.path中,一般安装的Python库的目录都可以在sys.path中找到(前提是将Python安装目录添加到电脑的环境变量中),所以对于安装好的库,我们直接导入即可.第二处是运行文件(这里是m1.py)所在的目录,因为m2.py和运行文件在同一个目录下,所以上面的写法没有问题。用上面的方法导入原来sys.path中的库是没有问题的。不过最好不要用上面的方法导入同目录下的文件!因为这可能会出错。演示这个错误需要import语句的第二种写法,所以我们先学习一下import的第二种写法。在Tree目录下新建目录Branch,在Branch中新建文件m3.py。m3.py的内容如下:defprintSelf():print('Inm3')如何在m1中导入m3.py,请看变化下面的m1.py:fromBranchimportm3m3.printSelf()总结import语句的第二种用法:frompackage_nameimportmodule_name。模块的集合通常称为包。与第一种写法类似,Python会在sys.path和运行文件的目录下寻找包,然后在包中导入名为module_name的模块。下面解释一下为什么不使用第一种import的写法来导入同目录下的文件。在Branch目录中新建一个m4.py文件。m4.py的内容如下:defprintSelf():print('Inm4')那么我们直接在m3.py中导入m4,m3.py就变成了:importm4defprintSelf():print('Inm3')这时候运行m1.py会报错,说无法导入m4模块。为什么?先看一下导入过程:m1使用fromBranchimportm3导入m3,然后在m3.py中使用importm4导入m4。看到问题了吗?m4.py和m1.py不在同一个目录下,怎么直接用importm4导入m4。(读者可以尝试直接在Tree目录下再创建一个m4.py文件,你会发现再次运行m1.py时不会报错,而是导入了第二个m4.py。)面对上述错误,使用python2运行m1.py不会报错,因为在python2中,上面提到的两个导入是相对导入,而在python3中,是绝对导入。话说回来,import最关键的部分就是involved——相对import和absoluteimport。还是说说python3的import用法。上述两种写法都是绝对导入,即用于导入sys.path中的包和运行文件所在目录下的包。对于sys.path中的包,这种写法是没有问题的;导入自己写的文件,如果是非运行入口文件(上面的m1.py是运行入口文件,可以使用绝对导入),则需要相对导入。比如对于非运行入口文件m3.py,m4.py的导入需要使用相对导入:from。importm4defprintSelf():print('Inm3')然后运行m1.py就可以了。列出相对导入的写法:from。导入模块名称。在与您自己相同的目录中导入模块。从.package_name导入模块名称。在与自身相同的目录中导入包的模块。从..导入模块名称。从父目录导入模块。从..package_name导入module_name。导入位于父目录中的包的模块。当然,可以有更多。每多一点,目录就多一层。不知道大家有没有注意上面那句话——“上面的m1.py是运行的入口文件,可以使用绝对导入”,这句话没有问题,和我平时的做法是一致的。那么,是否可以使用相对导入来运行入口文件呢?比如把m1.py的内容改成:from.Branchimportm3m3.printSelf()答案是可以的,但是不能使用pythonm1.py命令。相反,您需要进入Tree所在的目录并使用python-mTree.m1运行它。为什么?关于前者,PEP328提案中的一段似乎给出了原因:相对导入使用模块的名称属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为“__main__”),那么相对导入将被解析为就好像该模块是顶级模块一样,而不管该模块实际位于文件系统中的什么位置。不是很懂,但略懂一点。我们应该看过下面这段代码:if__name__=='__main__':main()意思是如果当前文件运行,__name__变量会被设置为__main__,然后执行main函数。如果文件作为模块导入,那么__name__就是模块名,不等于__main__,main函数不会被执行。比如上面修改后的m1.py,执行pythonm1.py命令后,会报如下错误:Traceback(mostrecentcalllast):File"m1.py",line1,infrom.分支导入m3ModuleNotFoundError:没有名为“_main_.Branch”的模块;'__main__'isnotapackage基于此,我猜测在执行pythonm1.py命令后,包'.'当前目录所代表的成为__main__。那为什么python-mTree.m1会起作用呢?台湾老师给出了解释:执行命令中的-m是让Python提前导入你想要的包或模块给你,然后执行脚本。即m1.py不被视为运行入口文件,也被视为导入模块,与非运行入口文件具有相同的性能。注意不能在Tree目录下运行python-mm1,会报ImportError:attemptedrelativeimportwithnoknownparentpackage的错误。因为m1.py中的from.Branchimportm3,解释器不知道是哪个包。使用python-mTree.m1,解释器就知道了。对应的包是Tree。反过来,如果m1.py使用绝对导入(fromBranchimportm3),是否可以用python-mm1运行?我试过了,如果当前目录是Tree。如果在其他目录下运行,比如Tree所在的目录(使用python-mTree.m1运行),是不行的。这可能仍然与绝对进口有关。(之前看到一个大型项目,它的运行入口文件有很多相对导入,傻傻的直接用python跑了,后来看到他给的samplerunningcommand有-m参数,现在突然实现了。)import的理解难度也差不多。让我们谈谈import的其他简单但实用的用法。导入moudle_name作为别名。有些module_names比较长,后面写起来比较麻烦,或者module_names可能有名字冲突,可以用as来重命名,比如importnumpyasnp.从模块名导入函数名、变量名、类名。以上所有导入都是整个模块。有时我们只想使用模块中的一些函数、一些变量和一些类。这种写法就够了。可以使用逗号导入模块中的多个元素。有时候引入的元素比较多,可以使用反斜杠来换行,官方推荐使用括号。fromTkinterimportTk,Frame,Button,Entry,Canvas,Text,\LEFT,DISABLED,NORMAL,RIDGE,END#反斜杠换行fromTkinterimport(Tk,Frame,Button,Entry,Canvas,Text,LEFT,DISABLED,NORMAL,RIDGE,END)#括号换行(推荐)说到这里,感觉import的核心已经说完了。那就跟着上面的博客说说使用import时可能遇到的问题。问题1的描述:ValueError:尝试相对导入超出顶级包。面对问题的第一步是熟悉它,最好是重现它,让它横亘在两个span之间,任由我们去践踏。还是上面四个文件,稍作修改,四个文件如下:#m1.pyfromBranchimportm3m3.printSelf()#m2.pydefprintSelf():print('module2')#m3.pyfrom..importm2#重现的关键就在这里#print(__name__)defprintSelf():print('Inm3')#m4.pydefprintSelf():print('Inm4')运行pythonm1.py,就会出现这个问题。有什么问题?我猜想,运行m1.py后,m1所代表的模块就是顶层模块(参见上面PEP328的参考),而m3.py试图导入的m2模块的包(也就是package所代表的Tree目录)的层级比m1高,所以会报这个错误。如何解决?将m1.py的所有导入改为相对导入,然后进入m1.py的上层目录,运行python-mTree.m1。其他使用import时出现的问题,遇到就更新。外部数据网址:[1]https://link.zhihu.com/?target...[2]https://link.zhihu.com/?target...本文仅供学习,版权归原作者所有,如有侵权请联系删除。学习Python的路上肯定会遇到困难,不要慌张,我这里有一套学习资料,包括40+电子书,800+教学视频,涉及Python基础、爬虫、框架、数据分析、机学习等等,别怕你学不会!https://shimo.im/docs/JWCghr8...《Python学习资料》关注公众号【蟒圈】,每日优质文章推送。