" />

亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python中import的用法陷阱解決盤點(diǎn)小結(jié)

 更新時(shí)間:2023年10月12日 09:52:17   作者:門書生  
這篇文章主要為大家介紹了Python中import的用法陷阱解決盤點(diǎn)小結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

Python用了快兩年了吧,其中有些東西一直是稀里糊涂地用,import便是我一直沒有明白的東西。曾經(jīng)有過三次解決它的機(jī)會(huì),我都因得過且過、一拖再拖而沒能化敵為友。今天下午,它又給了我一次機(jī)會(huì),我想我還是從了它的心愿吧。

故事是從這篇博客(Python 的 Import 陷阱)開始的,然后又跳到了Python社區(qū)的PEP 328提案(PEP 328 -- Imports: Multi-Line and Absolute/Relative),再結(jié)合過去的經(jīng)驗(yàn)以及一些測試,我想我大概懂了吧。下面是我的總結(jié),希望內(nèi)容能夠言簡意賅、易于理解。

import語句有什么用?

import語句用來導(dǎo)入其他python文件(稱為模塊module),使用該模塊里定義的類、方法或者變量,從而達(dá)到代碼復(fù)用的目的。為了方便說明,我們用實(shí)例來說明import的用法,讀者朋友可以跟著嘗試(嘗試時(shí)建議使用python3,python2和python3在import的表現(xiàn)有差異,之后會(huì)提到)。

將要建立文件的結(jié)構(gòu)為:

Tree
|____ m1.py
|____ m2.py
|____ Branch
     |____m3.py
     |____m4.py

首先,先建立一個(gè)文件夾Tree作為工作目錄,并在其內(nèi)建立兩個(gè)文件m1.py和m2.py,在m1.py寫入代碼:

import os
import m2
m2.printSelf()

在m2.py寫入代碼:

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

打開命令行,進(jìn)入到Tree目錄下,敲下python m1.py運(yùn)行,發(fā)現(xiàn)沒有報(bào)錯(cuò),且打印出In m2,說明這樣使用import沒有問題。由此我們總結(jié)出import語句的第一種用法。

  • import module_name。即import后直接接模塊名。在這種情況下,Python會(huì)在兩個(gè)地方尋找這個(gè)模塊
  • 第一是sys.path(通過運(yùn)行代碼import sys; print(sys.path)查看),os這個(gè)模塊所在的目錄就在列表sys.path中,一般安裝的Python庫的目錄都可以在sys.path中找到(前提是要將Python的安裝目錄添加到電腦的環(huán)境變量),所以對于安裝好的庫,我們直接import即可。
  • 第二個(gè)地方就是運(yùn)行文件(這里是m1.py)所在的目錄,因?yàn)閙2.py和運(yùn)行文件在同一目錄下,所以上述寫法沒有問題。

用上述方法導(dǎo)入原有的sys.path中的庫沒有問題。但是,最好不要用上述方法導(dǎo)入同目錄下的文件!因?yàn)檫@可能會(huì)出錯(cuò)。演示這個(gè)錯(cuò)誤需要用到import語句的第二種寫法,所以先來學(xué)一學(xué)import的第二種寫法。在Tree目錄下新建一個(gè)目錄Branch,在Branch中新建文件m3.py,m3.py的內(nèi)容如下:

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

如何在m1中導(dǎo)入m3.py呢,請看更改后的m1.py:

from Branch import m3
m3.printSelf()

總結(jié)import語句用法

  • from package_name import module_name。一般把模塊組成的集合稱為包(package)。與第一種寫法類似,Python會(huì)在sys.path和運(yùn)行文件目錄這兩個(gè)地方尋找包,然后導(dǎo)入包中名為module_name的模塊。

現(xiàn)在我們來說明為什么不要用import的第一種寫法來導(dǎo)入同目錄下的文件。在Branch目錄下新建m4.py文件,m4.py的內(nèi)容如下:

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

然后我們在m3.py中直接導(dǎo)入m4,m3.py變?yōu)椋?/p>

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

這時(shí)候運(yùn)行m1.py就會(huì)報(bào)錯(cuò)了,說沒法導(dǎo)入m4模塊。為什么呢?我們來看一下導(dǎo)入流程:m1使用from Branch import m3導(dǎo)入m3,然后在m3.py中用import m4導(dǎo)入m4。看出問題了嗎?m4.py和m1.py不在同一目錄,怎么能直接使用import m4導(dǎo)入m4呢。(讀者可以試試直接在Tree目錄下新建另一個(gè)m4.py文件,你會(huì)發(fā)現(xiàn)再運(yùn)行m1.py就不會(huì)出錯(cuò)了,只不過導(dǎo)入的是第二個(gè)m4.py了)

面對上面的錯(cuò)誤,使用python2運(yùn)行m1.py就不會(huì)報(bào)錯(cuò),因?yàn)樵趐ython2中,上面提到的import的兩種寫法都屬于相對導(dǎo)入,而在python3中,卻屬于絕對導(dǎo)入。話說到了這里,就要牽扯到import中最關(guān)鍵的部分了——相對導(dǎo)入和絕對導(dǎo)入。

我們還是談?wù)損ython3的import用法。上面提到的兩種寫法屬于絕對導(dǎo)入,即用于導(dǎo)入sys.path中的包和運(yùn)行文件所在目錄下的包。對于sys.path中的包,這種寫法毫無問題;導(dǎo)入自己寫的文件,如果是非運(yùn)行入口文件(上面的m1.py是運(yùn)行入口文件,可以使用絕對導(dǎo)入),則需要相對導(dǎo)入。

比如對于非運(yùn)行入口文件m3.py,其導(dǎo)入m4.py需要使用相對導(dǎo)入:

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

這時(shí)候再運(yùn)行m1.py就ok了。列舉一下相對導(dǎo)入的寫法:

  • from . import module_name。導(dǎo)入和自己同目錄下的模塊。
  • from .package_name import module_name。導(dǎo)入和自己同目錄的包的模塊。
  • from .. import module_name。導(dǎo)入上級目錄的模塊。
  • from ..package_name import module_name。導(dǎo)入位于上級目錄下的包的模塊。
  • 當(dāng)然還可以有更多的.,每多一個(gè)點(diǎn)就多往上一層目錄。

不知道你有沒有留神上面的一句話——“上面的m1.py是運(yùn)行入口文件,可以使用絕對導(dǎo)入”,這句話是沒問題的,也和我平時(shí)的做法一致。那么,運(yùn)行入口文件可不可以使用相對導(dǎo)入呢?比如m1.py內(nèi)容改成:

from .Branch import m3
m3.printSelf()

答案是可以,但不能用python m1.py命令,而是需要進(jìn)入到Tree所在的目錄,使用python -m Tree.m1來運(yùn)行。為什么?關(guān)于前者,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.

我不太懂,但是又有一點(diǎn)明白。我們應(yīng)該見過下面一段代碼:

if __name__ == '__main__':
	main()

意思是如果運(yùn)行了當(dāng)前文件,則__name__變量會(huì)置為__main__,然后會(huì)執(zhí)行main函數(shù),如果當(dāng)前文件是被其他文件作為模塊導(dǎo)入的話,則__name__為模塊名,不等于__main__,就不會(huì)執(zhí)行main函數(shù)。比如對于上述更改后的m1.py,執(zhí)行python m1.py命令后,會(huì)報(bào)如下錯(cuò)誤:

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

據(jù)此我猜測執(zhí)行python m1.py命令后,當(dāng)前目錄所代表的包'.'變成了__main__。

那為什么python -m Tree.m1就可以呢?那位臺灣老師給出了解釋:

執(zhí)行指令中的-m是為了讓Python預(yù)先import你要的package或module給你,然后再執(zhí)行script。

即不把m1.py當(dāng)作運(yùn)行入口文件,而是也把它當(dāng)作被導(dǎo)入的模塊,這就和非運(yùn)行入口文件有一樣的表現(xiàn)了。

注意,在Tree目錄下運(yùn)行python -m m1是不可以的,會(huì)報(bào) ImportError: attempted relative import with no known parent package的錯(cuò)誤。因?yàn)閙1.py中的from .Branch import m3中的. ,解釋器并不知道是哪一個(gè)package。使用python -m Tree.m1,解釋器就知道.對應(yīng)的是Tree這個(gè)package。

那反過來,如果m1.py使用絕對導(dǎo)入(from Branch import m3),能使用python -m m1運(yùn)行嗎?我試了一下,如果當(dāng)前目錄是Tree就可以。如果在其他目錄下運(yùn)行,比如在Tree所在的目錄(使用python -m Tree.m1運(yùn)行),就不可以。這可能還是與絕對導(dǎo)入相關(guān)。

(之前看到了一個(gè)大型項(xiàng)目,其運(yùn)行入口文件有一大堆的相對導(dǎo)入,我還傻乎乎地用python直接運(yùn)行它。之后看到他給的樣例運(yùn)行命令是帶了-m參數(shù)的?,F(xiàn)在才恍然大悟。)

理解import的難點(diǎn)差不多就這樣了。下面說一說import的其他簡單但實(shí)用的用法。

  • import moudle_name as alias。有些module_name比較長,之后寫它時(shí)較為麻煩,或者module_name會(huì)出現(xiàn)名字沖突,可以用as來給它改名,如import numpy as np。
  • from module_name import function_name, variable_name, class_name。上面導(dǎo)入的都是整個(gè)模塊,有時(shí)候我們只想使用模塊中的某些函數(shù)、某些變量、某些類,用這種寫法就可以了。使用逗號可以導(dǎo)入模塊中的多個(gè)元素。
  • 有時(shí)候?qū)氲脑睾芏?,可以使用反斜杠來換行,官方推薦使用括號。
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的核心已經(jīng)說完了。再跟著上面的博客說一說使用import可能碰到的問題吧。

import可能碰到的問題

問題1描述:ValueError: attempted relative import beyond top-level package。直面問題的第一步是去了解熟悉它,最好是能復(fù)現(xiàn)它,將錯(cuò)誤暴露在陽光之下。仍然是上面四個(gè)文件,稍作修改,四個(gè)文件如下:

# m1.py
from Branch import m3
m3.printSelf()
# m2.py
def printSelf():
	print('module2')
# m3.py
from .. import m2 # 復(fù)現(xiàn)的關(guān)鍵在這 #
print(__name__)
def printSelf():
	print('In m3')
# m4.py
def printSelf():
	print('In m4')

運(yùn)行python m1.py,就會(huì)出現(xiàn)該問題。問題何在?我猜測,運(yùn)行m1.py后,m1代表的模塊就是頂層模塊(參見上面PEP 328的引用),而m3.py中嘗試導(dǎo)入的m2模塊所在的包(即Tree目錄代表的包)比m1的層級更高,所以會(huì)報(bào)出這樣的錯(cuò)誤。怎么解決呢?將m1.py的所有導(dǎo)入改為相對導(dǎo)入,然后進(jìn)入m1.py的上層目錄,運(yùn)行python -m Tree.m1即可。

對于使用import出現(xiàn)的其他問題,碰到了再接著更新,更多關(guān)于Python import用法的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論