首頁  >  文章  >  後端開發  >  python中import學習備忘筆記

python中import學習備忘筆記

高洛峰
高洛峰原創
2017-02-03 16:05:201468瀏覽

前言

在python的模組有兩種組織方式,一種是單純的python文件,文件名就是模組名,一種是包,包是一個包含了若干python文件的目錄,目錄下必須有一個文件__init__.py,這樣目錄名字就是模組名,包裡的python檔案也可以透過包名.檔名的方式import

import語法

import語法有兩種

1、直接import模組

rrrereee

2、從模組import物件(下級模組,類,函數,變數等)

import Module
import Module as xx

as語法是用來設定物件(這裡用物件泛指模組,類,函數等等)別名,import將物件名字引入了當前文件的名字空間

假設有如下目錄結構

from Module import Name
from Module immport Name as yy

在當前目錄下,以下語句都是有效的

├── A.py
└── pkg
 ├── B.py
 └── __init__.py

為了簡化討論,下面將不會對as
import A
import pkg
import pkg.B
from pkg import B

為了簡化討論,下面將不會對as
import步驟

python所有載入的模組資訊都存放在sys.modules結構中,當import一個模組時,會依照下列步驟來進行

如果是import A,檢查sys.modules中是否已經有A,如果有則不加載,如果沒有則為A創建module對象,並加載A


如果是from A import B,先為A創建module對象,再解析A,從中尋找B並填充到A的__dict__中


嵌套import

在import模組時我們可能會擔心一個模組會不會被import多次,假設有A,B,C三個模組,A需要import B和C,B又要import C ,這樣A會執行到兩次import C,一次是自己本身import,一次是在import B時執行的import,但根據上面講到的import步驟,在第二次import時發現模組已經被加載,所以不會重複import

但如下情況卻會報錯

#filename: A.py
from B import BB
class AA:pass
 
#filename: B.py
from A import AA
class BB:pass

這時不管是執行A.py還是B.py都會拋出ImportError的異常,假設我們執行的是A.py,究其原因如下

文件A.py執行from B import BB,會先掃描B.py,同時在A的名字空間中為B創建module對象,試圖從B中查找BB

掃描B.py第一行執行from A import AA ,此時又會去掃描A.py


掃描A.py第一行執行from B import BB,由於步驟1已經為B創建module對象,所以會直接從B的module對象的__dict__中獲取BB ,此時顯然BB是取得不到的,於是拋出異常

解決這種情況有兩種辦法,

將from B import BB改為import B,或將from A import AA改為import A


將A.py或B.py中的兩行程式碼交換位置

總之,import需要注意的是,盡量在需要用到時再import


包的import

當一個目錄下有__init__ .py檔案時,目錄就是一個python的套件

import套件和import單一檔案是一樣的,我們可以這樣類比:

import單一檔案時,檔案裡的類,函數,變數都可以作為import的對象

import包時,包裡的子包,文件,以及__init__.py裡的類,函數,變數都可以作為import的對象

假設有如下目錄結構

pkg
├── __init__.py
└── file.py

其中__init__.py內容內容如下

argument = 0
class A:pass

在和pkg同級目錄下執行如下語句都是OK的

>>> import pkg
>>> import pkg.file
>>> from pkg import file
>>> from pkg import A
>>> from pkg import argument

但如下語句是錯誤的

>>> import pkg.A
>>> import pkg.argument

報錯詞,A和B都必須是模組(檔案或套件)


相對導入和絕對導入

絕對導入的格式為import A.B或from A import B,相對導入格式為from .import B或from ..A import B,.代表目前模組,..代表上層模組,...代表上上層模組,依次類推。當我們有多個套件時,就可能有需求從一個包import另一個包的內容,這就會產生絕對導入,而這也往往是最容易發生錯誤的時候,還是以具體例子來說明

目錄架構如下

app
├── __inti__.py
├── mod1
│ ├── file1.py
│ └── __init__.py
├── mod2
│ ├── file2.py
│ └── __init__.py
└── start.py


其中app/start.py內容為import mod1.file1 

app/mod1/file1.py內容為from ..mod2 import file2

為了方便分析,包括我們在所有py5 __init__.py)第一行加入print __file__, __name__

現在app/mod1/file1.py裡用到了相對導入,我們在app/mod1下執行python file1.py或者在app下執行python mod1/file1. py都會報錯ValueError: Attempted relative import in non-package

在app下執行python -m mod1.file1或python start.py都會報錯ValueError: Attempted relative import beyond toplevel package

再來看原因,我們先具體來看原因一下導入模組時的一些規則

在沒有明確指定包結構的情況下,python是根據__name__來決定一個模組在包中的結構的,如果是__main__則它本身是頂層模組,沒有包結構,如果是A.B.C結構,那麼頂層模組是A。

基本上遵循這樣的原則

如果是絕對導入,一個模組只能導入自身的子模組或和它的頂層模組同級別的模組及其子模組

如果是相對導入,一個模組必須有包結構且只能導入它的頂層模組內部的模組

有目錄結構如下

A
├── B1
│ ├── C1
│ │ └── file.py
│ └── C2
└── B2

其中A,B1,B2,C1,C2都为包,这里为了展示简单没有列出__init__.py文件,当file.py的包结构为A.B1.C1.file(注意,是根据__name__来的,而不是磁盘的目录结构,在不同目录下执行file.py时对应的包目录结构都是不一样的)时,在file.py中可采用如下的绝对的导入

import A.B1.C2
import A.B2

和如下的相对导入

from .. import C2
from ... import B2

什么情况下会让file.py的包结构为A.B1.C1.file呢,有如下两种

在A的上层目录执行python -m A.B1.C1.file, 此时明确指定了包结构

在A的上层目录建立文件start.py,在start.py里有import A.B1.C1.file,然后执行python start.py,此时包结构是根据file.py的__name__变量来的

再看前面出错的两种情况,第一种执行python file1.py和python mod1/file1.py,此时file.py的__name__为__main__ ,也就是说它本身就是顶层模块,并没有包结构,所以会报错

第二种情况,在执行python -m mod1.file1和python start.py时,前者明确告诉解释器mod1是顶层模块,后者需要导入file1,而file1.py的__name__为mod1.file1,顶层模块为也mod1,所以在file1.py中执行from ..mod2 import file2时会报错 ,因为mod2并不在顶层模块mod1内部。通过错误堆栈可以看出,并不是在start.py中绝对导入时报错,而是在file1.py中相对导入报的错

那么如何才能偶正确执行呢,有两种方法,一种是在app上层目录执行python -m app.mod1.file1,另一种是改变目录结构,将所有包放在一个大包中,如下

   
app
├── pkg
│ ├── __init__.py
│ ├── mod1
│ │ ├── __init__.py
│ │ └── file1.py
│ └── mod2
│ ├── __init__.py
│ └── file2.py
└── start.py

start.py内容改成import pkg.mod1.file1,然后在app下执行python start.py

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者使用python能带来一定的帮助,如有疑问大家可以留言交流。

更多python中import学习备忘笔记相关文章请关注PHP中文网!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn