Python中import的用法_python import-程序员宅基地

技术标签: python  深度学习  开发语言  

我的小程序:

待办计划:卷起来吧,少年!
我们记账:年薪50w够花么?

Python用了快两年了吧,其中有些东西一直是稀里糊涂地用,import便是我一直没有明白的东西。曾经有过三次解决它的机会,我都因得过且过、一拖再拖而没能化敌为友。今天下午,它又给了我一次机会,我想我还是从了它的心愿吧。

故事是从这篇台湾同胞的博客(Python 的 Import 陷阱)开始的,然后又跳到了Python社区的PEP 328提案(PEP 328 -- Imports: Multi-Line and Absolute/Relative),再结合过去的经验以及一些测试,我想我大概懂了吧。下面是我的总结,希望内容能够言简意赅、易于理解。

import语句有什么用?import语句用来导入其他python文件(称为模块module),使用该模块里定义的类、方法或者变量,从而达到代码复用的目的。为了方便说明,我们用实例来说明import的用法,读者朋友可以跟着尝试(尝试时建议使用python3,python2和python3在import的表现有差异,之后会提到)。

首先,先建立一个文件夹Tree作为工作目录,并在其内建立两个文件m1.py和m2.py,在m1.py写入代码:

import os
import m2
m2.printSelf()

在m2.py写入代码:

def printSelf():
	print('In m2')

打开命令行,进入到Tree目录下,敲下python m1.py运行,发现没有报错,且打印出In m2,说明这样使用import没有问题。由此我们总结出import语句的第一种用法。

  • import module_name。即import后直接接模块名。在这种情况下,Python会在两个地方寻找这个模块,第一是sys.path(通过运行代码import sys; print(sys.path)查看),os这个模块所在的目录就在列表sys.path中,一般安装的Python库的目录都可以在sys.path中找到(前提是要将Python的安装目录添加到电脑的环境变量),所以对于安装好的库,我们直接import即可。第二个地方就是运行文件(这里是m1.py)所在的目录,因为m2.py和运行文件在同一目录下,所以上述写法没有问题。

用上述方法导入原有的sys.path中的库没有问题。但是,最好不要用上述方法导入同目录下的文件!因为这可能会出错。演示这个错误需要用到import语句的第二种写法,所以先来学一学import的第二种写法。在Tree目录下新建一个目录Branch,在Branch中新建文件m3.py,m3.py的内容如下:

def printSelf():
	print('In m3')

如何在m1中导入m3.py呢,请看更改后的m1.py:

from Branch import m3
m3.printSelf()

总结import语句的第二种用法:

  • from package_name import module_name。一般把模块组成的集合称为包(package)。与第一种写法类似,Python会在sys.path和运行文件目录这两个地方寻找包,然后导入包中名为module_name的模块。

现在我们来说明为什么不要用import的第一种写法来导入同目录下的文件。在Branch目录下新建m4.py文件,m4.py的内容如下:

def printSelf():
	print('In m4')

然后我们在m3.py中直接导入m4,m3.py变为:

import m4
def printSelf():
	print('In m3')

这时候运行m1.py就会报错了,说没法导入m4模块。为什么呢?我们来看一下导入流程:m1使用from Branch import m3导入m3,然后在m3.py中用import m4导入m4。看出问题了吗?m4.py和m1.py不在同一目录,怎么能直接使用import m4导入m4呢。(读者可以试试直接在Tree目录下新建另一个m4.py文件,你会发现再运行m1.py就不会出错了,只不过导入的是第二个m4.py了)

面对上面的错误,使用python2运行m1.py就不会报错,因为在python2中,上面提到的import的两种写法都属于相对导入,而在python3中,却属于绝对导入。话说到了这里,就要牵扯到import中最关键的部分了——相对导入和绝对导入。

我们还是谈论python3的import用法。上面提到的两种写法属于绝对导入,即用于导入sys.path中的包和运行文件所在目录下的包。对于sys.path中的包,这种写法毫无问题;导入自己写的文件,如果是非运行入口文件(上面的m1.py是运行入口文件,可以使用绝对导入),则需要相对导入。

比如对于非运行入口文件m3.py,其导入m4.py需要使用相对导入:

from . import m4
def printSelf():
	print('In m3')

这时候再运行m1.py就ok了。列举一下相对导入的写法:

  • from . import module_name。导入和自己同目录下的模块。
  • from .package_name import module_name。导入和自己同目录的包的模块。
  • from .. import module_name。导入上级目录的模块。
  • from ..package_name import module_name。导入位于上级目录下的包的模块。
  • 当然还可以有更多的.,每多一个点就多往上一层目录。

不知道你有没有留神上面的一句话——“上面的m1.py是运行入口文件,可以使用绝对导入”,这句话是没问题的,也和我平时的做法一致。那么,运行入口文件可不可以使用相对导入呢?比如m1.py内容改成:

from .Branch import m3
m3.printSelf()

答案是可以,但不能用python m1.py命令,而是需要使用python -m m1来运行。为什么?关于前者,PEP 328提案中的一段文字好像给出了原因:

Relative imports use a module's _name _ attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to '__main __') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

我不太懂,但是又有一点明白。我们应该见过下面一段代码:

if __name__ == '__main__':
	main()

意思是如果运行了当前文件,则__name__变量会置为__main__,然后会执行main函数,如果当前文件是被其他文件作为模块导入的话,则__name__为模块名,不等于__main__,就不会执行main函数。比如对于上述更改后的m1.py,执行python m1.py命令后,会报如下错误:

Traceback (most recent call last): File "m1.py", line 1, in from .Branch import m3 ModuleNotFoundError: No module named '_ main_.Branch'; '__main__' is not a package

据此我猜测执行python m1.py命令后,当前目录所代表的包'.'变成了__main__。

那为什么python -m m1就可以呢?那位台湾老师给出了解释:

执行指令中的-m是为了让Python预先import你要的package或module给你,然后再执行script。

即不把m1.py当作运行入口文件,而是也把它当作被导入的模块,这就和非运行入口文件有一样的表现了。

那反过来,如果m1.py使用绝对导入(from Branch import m3),能使用python -m m1运行吗?我试了一下,如果当前目录是Tree就可以。如果在其他目录下运行,比如在Tree所在的目录(使用python -m Tree.m1运行),就不可以。这可能还是与绝对导入相关。

(之前看到了一个大型项目,其运行入口文件有一大堆的相对导入,我还傻乎乎地用python直接运行它。之后看到他给的样例运行命令是带了-m参数的。现在才恍然大悟。)

理解import的难点差不多就这样了。下面说一说import的其他简单但实用的用法。

  • import moudle_name as alias。有些module_name比较长,之后写它时较为麻烦,或者module_name会出现名字冲突,可以用as来给它改名,如import numpy as np
  • from module_name import function_name, variable_name, class_name。上面导入的都是整个模块,有时候我们只想使用模块中的某些函数、某些变量、某些类,用这种写法就可以了。使用逗号可以导入模块中的多个元素。
  • 有时候导入的元素很多,可以使用反斜杠来换行,官方推荐使用括号。
from Tkinter import Tk, Frame, Button, Entry, Canvas, Text, \
    LEFT, DISABLED, NORMAL, RIDGE, END	# 反斜杠换行
from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
    LEFT, DISABLED, NORMAL, RIDGE, END)	# 括号换行(推荐)

说到这感觉import的核心已经说完了。再跟着上面的博客说一说使用import可能碰到的问题吧。

问题1描述:ValueError: attempted relative import beyond top-level package。直面问题的第一步是去了解熟悉它,最好是能复现它,让它躺在两跨之间任我们去践踏蹂躏。仍然是上面四个文件,稍作修改,四个文件如下:

# m1.py
from Branch import m3
m3.printSelf()
# m2.py
def printSelf():
	print('module2')
# m3.py
from .. import m2 # 复现的关键在这 #
print(__name__)
def printSelf():
	print('In m3')
# m4.py
def printSelf():
	print('In m4')

运行python m1.py,就会出现该问题。问题何在?我猜测,运行m1.py后,m1代表的模块就是顶层模块(参见上面PEP 328的引用),而m3.py中尝试导入的m2模块所在的包(即Tree目录代表的包)比m1的层级更高,所以会报出这样的错误。怎么解决呢?将m1.py的所有导入改为相对导入,然后进入m1.py的上层目录,运行python -m Tree.m1即可。

对于使用import出现的其他问题,碰到了再接着更新。

待办计划:卷起来吧,少年!
我们记账:年薪50w够花么?
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_35732969/article/details/104703625

智能推荐

jq对象与js对象_js对象中使用jq-程序员宅基地

文章浏览阅读2.9k次,点赞2次,收藏3次。jquery中的$(“#id”)与document.getElementById(“id”)的区别以前一直认为jquery中的$(“#id”)和document.getElementByIdx_x(“id”)得到的效果是一样的,今天做特效的时候才发现并不是这么一回事,通过测试得到:1、alert($(“#div”))得到的是[object Object]2、alert(document_js对象中使用jq

Unity 100面试题_unity100题-程序员宅基地

文章浏览阅读995次。原文_unity100题

12.路由安装-程序员宅基地

文章浏览阅读1k次,点赞29次,收藏11次。【校园二手交易平台】(微信小程序)安装vscode使用vscode打开后台系统项目在终端运行即可运行项目src/assets中存放静态资源src/components中存放组件app.vue是主界面(入口页面)注释main.ts中的package.json中是项目依赖的版本号注意安装插件README.md是项目启动的方式。

产品设计体会(3013)项目的“敏捷沟通”实践-程序员宅基地

文章浏览阅读48次。我一直觉得敏捷是理想与现实妥协的结果,是一种很好的实践,理论网上随便一搜就有很多,这次就说说我身边的团队,真实的实践,通过“沟通”的角度来讲,不妨起个名字叫做“敏捷沟通”。我们的每个项目,项目经理都会建立一个临时的IM群(旺旺)、一个临时的邮件列表,把项目干系人全部加入。邮件列表通常是通过第一封项目相关的邮件,把大家的email整理齐,在邮件最后说明“..._产品模块设计沟通会

Linux 设备驱动管理之内核对象(Kernel Object)机制-程序员宅基地

文章浏览阅读730次,点赞25次,收藏13次。内核对象是Linux内核中的一个基本抽象,用于表示内核中的所有对象,如设备、模块、文件系统等。它不仅是系统资源的代表,还提供了一种标准化的方式来管理这些资源。内核对象(kobject)是Linux内核中用于管理和表示内核资源的抽象基础结构。kobject机制与sysfs紧密集成,提供了一种标准化的方法来管理不同类型的内核资源,并使内核能够以一种可扩展和动态的方式与用户空间交互。理解kobject对于开发和理解Linux设备驱动至关重要。

全网首发,Python解决某象滑动还原验证码100%还原_python还原验证图片-程序员宅基地

文章浏览阅读1.1k次。与一般的滑动验证码不同,某象的滑动还原验证码是将图像上下两块分割,然后在随机一块往右移动,将两块拼图移动成完整的图像才算成功,事实上,解决这类验证码比普通的验证码还要简单。_python还原验证图片

随便推点

AGV叉车式智能运载机器人_转塔数控车床_混凝土罐车3D_仓库托盘运输小车_汽车装饰盖板模具设计_AGV叉车式智能运载机器人_汽车灯透镜片模具_数控车床 Creo 设计_地摊摆摊小推车模型……_agv机器人3d模型-程序员宅基地

文章浏览阅读228次。Ottawa公共汽车公交车模型3D图纸 Solidworks设计 附STEP。倒骑驴三轮车简易模型3D图纸 Solidworks设计 附STEP。荷兰铁路欧洲快车模型3D图纸 Solidworks设计 附工程图。仓库托盘运输小车3D图纸 Solidworks设计 附STEP。摩托车前悬架3D数模图纸 Solidworks设计 附IGS。蒸汽机车模型3D图纸 Solidworks设计 附平面图。简易宝马X5汽车3D模型图纸 Solidworks设计。拖车结构3D图纸 Solidworks设计 附STEP。_agv机器人3d模型

iOS开发经验总结(三)_-[nsmutableparagraphstyle renderingmode]:-程序员宅基地

文章浏览阅读321次。1、设置UILabel行间距12345NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:label.text]; NSMutableParagraphStyle *sty_-[nsmutableparagraphstyle renderingmode]:

近日学习总结-程序员宅基地

文章浏览阅读49次。毫无疑问,intel DRD部门面跪了,加上上次的DataCenter,现在已经是败上加败,加上近日看书效率太低,很多时候在同一个知识点打转,今天一天几乎没有看什么实质性内容。我觉得自己有几点需要注意:一、端正思想,开弓没有回头箭,既然选择了转行,选择了JAVA,即便是逆流而上,也要义无反顾的走下去!二、必须走一条高速、高效的道路。最重要的是第二点,让自己每天都能看到自己的进步,每...

JSP中<c:foreach></c:foreach>标签注意事项_jsp <c:for>-程序员宅基地

文章浏览阅读160次。c:foreach>是我们在jsp页面中处理循环是所使用的标签如下,但是items中必须是一个集合,否则就会报错。_jsp

IRQ中断服务函数_gtmr_irqhandler()-程序员宅基地

文章浏览阅读207次。IRQ中断服务函数是由操作系统或开发人员编写的函数,当IRQ中断请求到达服务器时,处理器会暂停当前任务,保存上下文,并跳转到相应的中断服务函数执行对应操作。_gtmr_irqhandler()

报错:“不是有效的Win32应用程序”的解决办法-程序员宅基地

文章浏览阅读937次。Win7、Win8下用VS2013编译完的程序,拿到32位WindowsXP虚拟机下运行有时候会报错:解决办法如下:进入工程的属性,修改:再修改:即可转载于:https://www.cnblogs.com/predator-wang/p/4788814.html..._tts speak 不是有效的win32应用程序