当我们在IDE(如PyCharm)中写python代码时,用形如import package.code
来调用其它包中的代码是一件平时写程序时习以为常的一件事情,即使在不同文件夹的路径或者不同层级,由于IDE加载了整个工程,所以在IDE中写程序时是能够索引到工程的根目录的,因此我们的代码通常是可以跑通的。如以下程序结构:
在这样一个工程中,在工程的根目录下,有两个包,其中一个时code_for_run,另一个是package_for_import。这里我们用第一个文件夹来模拟我们要跑的程序的所在路径,在其中为了更好地表示效果,又加了一层dir_inner,之后的测试用的代码就在这个路径之下;而第二个包为我们所需要调用的包的路径。
首先,在第二个包中用文件code_outer.py来表示一个我们要调用的文件,其中的内容为:
str_in_code_outer = "Successfully open file"
即,其中仅仅定义了一个字符串,而我们的测试结果便是是否可以成功地将这个字符串打印出来。
当我们在PyCharm中码代码时,在dir_inner之下的test.py文件中输入以下代码:
from package_for_import.code_outer import *
print str_in_code_outer
不仅IDE不会对代码标红来提示错误,而且是可以成功运行程序的:
以上便是在IDE中的情况了,但是在很多场景中,我们只是在PyCharm中来编写程序,最终是要部署到服务器上来运行的,这时,这些代码还可以成功运行么?
我们可以在cmd中模拟这种情况,不经过IDE来直接运行代码:
可以看到,在cmd中运行这段代码,结果报错ImportError: No module named package_for_import.code_outer
,即找不到所需要的包。因此,可以看到,我们这样简单的调用方式是不能应用于这些场景的。为了满足代码尽可能健壮、可移植、可部署的要求,我们需要对代码进行改进。
那么,首先分析,为什么会调用不到这个包呢,首先我们要知道,python中,程序如果要使用别的文件,无论是另一个包,另一个程序还是另一个文本文件,如果没有标绝对路径的话,是会在所运行的程序所在的路径和系统环境变量路径中查找的,从这一点来思考的话,原因就比较容易找到了,之所以报错,就是因为在这两个路径中是找不到package_for_import
这个包的。
那么,为什么之前在PyCharm中可以成功地运行呢?就如在本文开头所说,IDE会去索引工程的根目录,因此是可以找到package_for_import
这个包的,而若要找的文件的第一层包不是在工程的根目录,即使在IDE中也不能保证是可以运行的(如在PyCharm中便是会报错的)。
既然知道了出错的原因,接下来就是如何去改进我们的代码了。一个比较直接的思路就是将所需要的路径添加进程序运行时会索引到的路径的集合中。 改进的代码如下:
import sys
from os.path import dirname, abspath
path = dirname(dirname(dirname(abspath(__file__))))
sys.path.append(path)
from package_for_import.code_outer import *
print str_in_code_outer
运行结果:
可以看到,我们已经成功地运行了程序,下面简单讲一下增加的这段代码的原理。
其中的sys.path
就是系统的环境变量里的path
中的路径,而程序中底下的abspath(__file__)
就是得到本文件所在的绝对路径,而dirname就是不断地去得到上一层,下面笔者将其中的每一步的中间步骤的结果打印了出来,可以一目了然地看到操作的过程:
import sys
from os.path import dirname, abspath
path = dirname(dirname(dirname(abspath(__file__))))
print "sys.path:\t" + str(sys.path)
print "abspath(__file__):\t\t\t\t\t\t" + abspath(__file__)
print "dirname(abspath(__file__)):\t\t\t\t" + dirname(abspath(__file__))
print "dirname(dirname(abspath(__file__))):\t" + dirname(dirname(abspath(__file__)))
sys.path.append(path)
from package_for_import.code_outer import *
print str_in_code_outer
输出结果:
sys.path: ['D:\\codes\\python_test\\code_for_run\\dir_inner', 'D:\\codes\\python_test', 'C:\\Users\\LenFranky\\Anaconda2\\python27.zip', 'C:\\Users\\LenFranky\\Anaconda2\\DLLs', 'C:\\Users\\LenFranky\\Anaconda2\\lib', 'C:\\Users\\LenFranky\\Anaconda2\\lib\\plat-win', 'C:\\Users\\LenFranky\\Anaconda2\\lib\\lib-tk', 'C:\\Users\\LenFranky\\Anaconda2', 'C:\\Users\\LenFranky\\Anaconda2\\lib\\site-packages', 'C:\\Users\\LenFranky\\Anaconda2\\lib\\site-packages\\win32', 'C:\\Users\\LenFranky\\Anaconda2\\lib\\site-packages\\win32\\lib', 'C:\\Users\\LenFranky\\Anaconda2\\lib\\site-packages\\Pythonwin', 'E:\\PyCharm 2018.1.4\\helpers\\pycharm_matplotlib_backend']
abspath(__file__): D:\codes\python_test\code_for_run\dir_inner\test.py
dirname(abspath(__file__)): D:\codes\python_test\code_for_run\dir_inner
dirname(dirname(abspath(__file__))): D:\codes\python_test\code_for_run
Successfully open file