Python中import使用记录

目录

前言

需求及问题

需求描述

解决方案

个人理解

参考文献


前言

       上一次系统性学习python还是2017年,当时主要是觉得应该学点什么,但是没有很明确的目标性,跟着廖大神的博客边学边练,一直到看到手写web服务那块,因为没有实际的项目练手,当时学了一段时间,就扔掉了。最近半年因为一些其他原因,又重新将python拾了起来,有具体的业务问题需要解决,开发中遇到的问题就比较多。 今天主要记录下关于import模块或者包的时候遇到的问题,通过对比java中的import来记录下我自己的一些个人理解。

需求及问题

       Python版本:3.7

       开发工具:Pycharm

       核心诉求:

       python和java有些类似,但是比java灵活的多,很多时候可能想做单元测试,就直接在某个model中写个main方法直接去运行然后验证,但整个工程的实际入口可能是最外层的某个model。主入口不同,model的默认目录地址不同,在子目录进行import的时候及单元测试的时候,总是会遇到各种modelError的问题,关于import使用的过程中,最常见遇到的报错信息如下三种:

       (1)找不到模块

ModuleNotFoundError: No module named 'xxx'

       遇到这种情况一般有两种情况,要么是写代码的时候手误,写错了名字。要么就是sys.path中没有该model对应的路径。第一种简单,修改名字即可。如果是第二种情况,那为什么会找不到model,为什么model对应的路径没有在os.path呢?造成这个问题的原因主要还在于入口model所在的路径。main方法对应的model的路径就是默认路径,如果涉及子目录,子包,在子包中又有import,这种情况很容出现,尤其在pycharm中代码不会报错,但是因为启动的main方法对应model所在目录不同,级联import的时候很容易出现该问题。主要原因还是因为model的路径没有再sys.path中。解决方法我们后面再摆。

       (2)尝试在没有已知父包的情况下进行相对导入

ImportError: attempted relative import with no known parent package

       根据这个错误描述其实不难发现,出现这个问题,肯定是使用了相对路径,比如 .xx或者..xx的方式去引入包了,但是最上层或者某一层并不是python的package包,所以会hi有这个问题。

       (3)试图在顶级包之外进行相对导入

ValueError: attempted relative import beyond top-level package

       出现这个问题,肯定也是使用了.或者..进行相对路径import了,但是因为main方法执行的model所在路径不同,会出现这样的问题。

本篇我会记录和描述以下几种情况的引用:

(1)同级package内的model进行import和调用,并支持直接main方法执行

(2)import同级package下的model,该model又import了和它自己同级的其他model

(3)main方法在第一层的model,import的model有多层级,深层import场景

我们先来看一个工程的包目录结构,如下图:

       这是一个很简单的demo工程,str1、st2、st3、stduy四个package下对应的model都非常简单,func都是简单的print用于验证调用关系。

需求描述

(1)main.py中调用f1.py,f1.py会需要调用f2.py、py2.py、py3.py

(2)不管在哪一级,都需要支持随时随地的main方法的测试和执行

(3)在pycharm中不需要修改代码的情况下,任何层级作为main入口都可以正常运行

解决方案

       想要从根本上解决各种import的问题,重点还是需要搞清楚问题的主要原因。 不管是上面提到的那种问题,最直接的原因主要还是model对应的目录没有再sys.path中,python找不到对应的model。因为python执行的时候,默认的model对应的目录除了系统变量层面的,工程层面的目录就是入口的model对应的目录,而子目录是不会被加载进去的。 所以子目录中如果没有使用相对路径引入,就会找不到对应的model。但是如果使用相对路径引入,就没办法在开发的过程中直接运行main方法在各个子model进行单元测试,因为import的相对路径不对,大概率会出现上面提到后面两种问题。

       关于这些问题,有两种解决思路,如下,
       (1)通过sys.path.append和os.path在对应子model中将其对应的路径添加到sys.path中,在import的时候,可以直接import,而不用使用相对路径。(注意:这种方法如果在pycharm中开发,可能会遇到编译器报错,但是运行没错的现象,主要是编译器会对工程设置默认的根目录,它预校验的时候是按照设定的根目录去校验的,所以可能会报找不到model,但是运行又没事)。

比如在main.py中调用f1,f1中调用py2,py2调用py3,代码如下:

#main.py
import study.f1 as f1
if __name__ == '__main__':
    f1.showtest()

#f1.py
from study import f2
from st2 import py2
def showtest():
    f2.sec()
    py2.test2()

#py2.py
from st3 import py3
def test2():
    print('in st2.py2')
    py3.test3()

#py3.py
def test3():
    print('in st2.str3.py3')

       按照如上方式写,在pycharm中预校验不报错,但是运行的时候,会报第一个错误,如下:

 

       主要原因是因为项目目录默认到pyStudy目录,所以study和st2目录下的model都没问题,但是st2/st3的目录不存在,所以报py3对应的st3这个module不存在。解决方法是尝试将st3目录加到sys.path里,修改py2.py的代码,如下:

#py2.py
import os
import sys

print(os.getcwd())
sys.path.append(os.path.join(os.getcwd(), 'st2'))

from st3 import py3

def test2():
    print('in st2.py2')
    py3.test3()

       再重新到main方法中执行,可以了。

       (2)上面的方案其实就是将对应model添加到对应的sys.path中解决的。但是如果按照如上代码,在f1.py中直接运行的话,还是会发现有问题,因为默认的model路径变成了pyStudy/study了,st2/st3的目录没有,并且通过os.getcwd()获取的路径还是到pyStudy/study,所以想通过join方式拼接也比较麻烦。 这也不符合在任意py文件中运行进行单元测试的预期。

       在java中import包路径的时候时候是import的全路径,例如:

       java.打头的是jdk相关的,类似系统变量的,对应python中的类似 os/sys这样的模块。com.打头的是当前工程的包目录,可以看到java中import类的时候时候走的全路径。 基于这个思路,python中是否也可以呢? 

       我们先查看下当前工程的根目录是什么,File--->Settings--->Project:pyStudy--->Project Structure:

 

       可以看到根目录是pyStudy,那就意味str1/str2/study这三个package下的model是可以被直接引用到的,但是str2/str3下的不行,我们尝试修改py2.py代码如下:

from st2.st3 import py3

def test2():
    print('in st2.py2')
    py3.test3()

       直接运行main.py和f1.py都没问题。

个人理解

       两种方案,第二种肯定是最优的解决方案,也就是说,在import的时候,直接从根目录下的package包名开始写,这样不需要考虑相对引用,也不需要考虑听过sys.path维护解决问题。比较彻底的解决了诉求。

参考文献

【1】Python 3.x | 史上最详解的 导入(import)

【2】Python教程-廖雪峰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值