0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Python imports指南:Python的導(dǎo)入有更好的理解

馬哥Linux運(yùn)維 ? 來源:未知 ? 作者:李倩 ? 2018-05-01 17:48 ? 次閱讀

聲明:如果你每天寫Python,你會(huì)發(fā)現(xiàn)這篇文章中沒有新東西。 這是專為那些像運(yùn)維人員等偶爾使用Python的人以及那些忘記/誤用python import的人寫的。 盡管如此,代碼是用Python 3.6類型注釋編寫的,以滿足有經(jīng)驗(yàn)的Python讀者。 像往常一樣,如果你發(fā)現(xiàn)任何錯(cuò)誤,請(qǐng)告訴我!

模塊

我們從一個(gè)常見的python代碼開始

if __name__ == '__main__': invoke_the_real_code()

很多人,我也不例外,把它當(dāng)成固定格式,而不去深入理解它。 我們已經(jīng)知道一點(diǎn),當(dāng)從CLI調(diào)用你的代碼而不是導(dǎo)入它時(shí),這個(gè)代碼片段會(huì)有所不同。 現(xiàn)在讓我們?cè)囍ダ斫馕覀優(yōu)槭裁葱枰盟?/p>

為了說明,假設(shè)我們正在編寫一款披薩店軟件。 源碼在Github上。 這是pizza.py文件。

# pizza.py fileimport mathclass Pizza: name: str = '' size: int = 0 price: float = 0 def __init__(self, name: str, size: int, price: float) -> None: self.name = name self.size = size self.price = price def area(self) -> float: return math.pi * math.pow(self.size / 2, 2) def awesomeness(self) -> int: if self.name == 'Carbonara': return 9000 return self.size // int(self.price) * 100print('pizza.py module name is %s' % __name__)if __name__ == '__main__': print('Carbonara is the most awesome pizza.')

我已經(jīng)添加了打印__name__變量的代碼,以便了解__name__是如何變化的。

$ python3 pizza.pypizza.py module name is __main__Carbonara is the most awesome pizza.

的確,全局變量__name__在從CLI調(diào)用的時(shí)候設(shè)置成了“__main__”。

可是如果從另外一個(gè)文件中引用它會(huì)怎么樣呢?以下是menu.py的源碼:

# menu.py filefrom typing import Listfrom pizza import PizzaMENU: List[Pizza] = [ Pizza('Margherita', 30, 10.0), Pizza('Carbonara', 45, 14.99), Pizza('Marinara', 35, 16.99),]if __name__ == '__main__': print(MENU)

運(yùn)行menu.py

$ python3 menu.pypizza.py module name is pizza[, , ]

接著看看下面兩點(diǎn):

pizza.py代碼中的第一條打印語句在import的時(shí)候執(zhí)行了。

pizza.py代碼中的全局變量__name__設(shè)置成了沒有.py后綴的文件名。

所以,事實(shí)是,__name__是保存當(dāng)前Python模塊名稱的全局變量。

模塊名稱由解釋器在__name__變量中設(shè)置

當(dāng)從CLI調(diào)用模塊時(shí),其名稱被設(shè)置為__main__

那么到底什么是模塊呢? 這非常簡單 - 模塊是一個(gè)包含Python代碼的文件,可以使用解釋器(python程序)執(zhí)行或從其他模塊導(dǎo)入。

Python模塊只是一個(gè)包含Python代碼的文件

就像執(zhí)行時(shí)一樣,當(dāng)模塊被導(dǎo)入時(shí),它的頂級(jí)語句也會(huì)被執(zhí)行,但是要知道,即使從不同的文件中導(dǎo)入它幾次,它也只會(huì)被執(zhí)行一次。

當(dāng)你導(dǎo)入模塊時(shí),它會(huì)被執(zhí)行

因?yàn)槟K只是純文件,所以有一個(gè)簡單的方法來導(dǎo)入它們。 只取文件名,去掉.py擴(kuò)展名并將其放入import語句中。

要導(dǎo)入模塊,請(qǐng)使用不帶.py擴(kuò)展名的文件名

有趣的是,__name__被設(shè)置為文件名,無論你如何導(dǎo)入它 - 例如import pizza as broccoli,__name__仍然是pizza。 所以

導(dǎo)入時(shí),即使使用import module as othername將模塊名稱重命名,模塊名稱仍然設(shè)置為不帶.py擴(kuò)展名的文件名

但是如果導(dǎo)入的模塊不在同一個(gè)目錄下,我們?cè)趺磳?dǎo)入呢? 答案是放在模塊搜索路徑中,我們最終會(huì)在討論包時(shí)研究它。

包是模塊集合的名稱空間

命名空間部分很重要,因?yàn)樗旧聿⒉惶峁┤魏喂δ?- 它只是給你一個(gè)組合你所有模塊的方式。

兩種情況下,你需要把模塊放入一個(gè)包中。 首先是隔離一個(gè)模塊的定義。 在我們的pizza模塊中,我們有一個(gè)可能與其他Pizza包相沖突的Pizza類(我們?cè)趐ypi上有一些pizza包)

第二種情況是,如果你想分發(fā)你的代碼,因?yàn)?/p>

包是Python中最小的代碼分發(fā)單元

你在PyPI上看到的所有東西都是通過pip安裝的,所以為了分享你的東西,你必須把它做成一個(gè)包。

好吧,假設(shè)我們確信并想將我們的2個(gè)模塊轉(zhuǎn)換成一個(gè)很好的包。 要做到這一點(diǎn),我們需要?jiǎng)?chuàng)建一個(gè)包含一個(gè)空的__init__.py文件的目錄,并將我們的文件移入該目錄:

pizzapy/├── __init__.py├── menu.py└── pizza.py

就是這樣 - 現(xiàn)在你有一個(gè)比薩餅包!

要?jiǎng)?chuàng)建一個(gè)包,創(chuàng)建一個(gè)包含__init__.py文件的目錄

請(qǐng)記住,程序包是模塊的名稱空間,因此您不會(huì)導(dǎo)入包本身,而是從包中導(dǎo)入模塊。

>>> import pizzapy.menupizza.py module name is pizza>>> pizzapy.menu.MENU[, , ]

如果以這種方式進(jìn)行導(dǎo)入,則可能看起來過于冗長,因?yàn)槟枰褂猛耆薅ǖ拿Q。 我猜這是有意為之,因?yàn)镻ython宗旨之一是“明確比隱含更好”。

無論如何,你總是可以使用from package import module的格式來縮短名稱:

>>> from pizzapy import menupizza.py module name is pizza>>> menu.MENU[, , ]

包初始化

還記得我們?nèi)绾伟岩粋€(gè)__init__.py文件放在一個(gè)目錄中,這個(gè)目錄就神奇地變成了一個(gè)包嗎?這是一個(gè)很好的慣例配置示例,我們不需要描述任何配置或注冊(cè)任何東西。約定包含__init__.py的任何目錄都是Python包。

除了標(biāo)識(shí)一個(gè)包,__init__.py還有一個(gè)目的 - 包初始化。這就是為什么它被稱為init!初始化是在包導(dǎo)入時(shí)觸發(fā)的,換句話說,導(dǎo)入包時(shí)調(diào)用__init__.py

當(dāng)你導(dǎo)入一個(gè)包時(shí),包內(nèi)的__init__.py模塊被執(zhí)行

在__init__模塊中,你可以做任何你想做的事情,但最常用的是用于一些包初始化或設(shè)置專用的__all__變量。后者控制*(通配符)導(dǎo)入 - from package import *。

而且因?yàn)镻ython很棒,我們可以在__init__模塊中做很多事情,甚至是很奇怪的事情。假設(shè)我們不喜歡顯式導(dǎo)入,并且希望將所有模塊符號(hào)上升到包級(jí)別,這樣我們就不必記住實(shí)際的模塊名稱。

為此,我們可以在__init__.py中像這樣導(dǎo)入menu和pizza模塊中的所有東西

# pizzapy/__init__.pyfrom pizzapy.pizza import *from pizzapy.menu import *

看看運(yùn)行結(jié)果:

>>> import pizzapypizza.py module name is pizzapy.pizzapizza.py module name is pizza>>> pizzapy.MENU[, , ]

沒有更多的pizzapy.menu.Menu或menu.MENU :-)這種方式有點(diǎn)像Go中的軟件包,但請(qǐng)注意,你正試圖濫用Python,不鼓勵(lì)這樣做,因?yàn)樵谀阋a檢查時(shí),會(huì)讓你抓狂的。 別怪我哦,我只是為了舉例說明!

您可以像這樣更簡潔地重寫導(dǎo)入

# pizzapy/__init__.pyfrom .pizza import *from .menu import *

這只是另一種做同樣事情的語法,就是所謂的相對(duì)導(dǎo)入。 我們來仔細(xì)看看。

絕對(duì)和相對(duì)導(dǎo)入

上面的2個(gè)代碼段是做所謂的相對(duì)導(dǎo)入的唯一方法,因?yàn)樽訮ython 3開始,所有導(dǎo)入都默認(rèn)為絕對(duì)導(dǎo)入(如在PEP328中),這意味著導(dǎo)入將嘗試首先導(dǎo)入標(biāo)準(zhǔn)模塊,然后才導(dǎo)入本地包。 在創(chuàng)建自己的sys.py模塊時(shí),需要避免使用標(biāo)準(zhǔn)模塊的名稱,因?yàn)閕mport sys可以覆蓋標(biāo)準(zhǔn)庫sys模塊。

自Python 3開始,所有導(dǎo)入都默認(rèn)為絕對(duì)導(dǎo)入 - 它將首先查找系統(tǒng)包

但是如果你的軟件包有一個(gè)名為sys的模塊,并且你想把它導(dǎo)入到同一個(gè)包內(nèi)的另一個(gè)模塊中,你必須做相對(duì)的導(dǎo)入。 要做到這一點(diǎn),你必須再次明確的這樣寫package.module import somesymbol或from .module import somesymbol。 模塊名稱之前的那個(gè)有趣的點(diǎn)理解為“當(dāng)前包”。

要進(jìn)行相對(duì)導(dǎo)入,請(qǐng)?jiān)谀K名前加上程序包名稱或點(diǎn)

可執(zhí)行程序包

在Python中,您可以使用python3 -m 構(gòu)造調(diào)用模塊。

$ python3 -m pizzapizza.py module name is __main__Carbonara is the most awesome pizza.

然而也可以這樣調(diào)用:

$ python3 -m pizzapy/usr/bin/python3: No module named pizzapy.__main__; 'pizzapy' is a package and cannot be directly executed

如你所看到的,這需要一個(gè)__main__模塊,因此要先實(shí)現(xiàn)它:

# pizzapy/__main__.pyfrom pizzapy.menu import MENUprint('Awesomeness of pizzas:')for pizza in MENU: print(pizza.name, pizza.awesomeness())

現(xiàn)在可以正常使用了:

$ python3 -m pizzapypizza.py module name is pizzaAwesomeness of pizzas:Margherita 300Carbonara 9000Marinara 200

添加__main__.py使包可執(zhí)行(使用python3 -m package調(diào)用它)

導(dǎo)入兄弟包

而我想要涵蓋的最后一件事是導(dǎo)入兄弟包。 假設(shè)我們有一個(gè)兄弟包pizzashop:

.├── pizzapy│ ├── __init__.py│ ├── __main__.py│ ├── menu.py│ └── pizza.py└── pizzashop ├── __init__.py └── shop.py

# pizzashop/shop.pyimport pizzapy.menuprint(pizzapy.menu.MENU)

現(xiàn)在,位于頂層目錄下,如果我們?cè)噲D像這樣調(diào)用shop.py

$ python3 pizzashop/shop.pyTraceback (most recent call last): File "pizzashop/shop.py", line 1, in import pizzapy.menuModuleNotFoundError: No module named 'pizzapy'

我們得到了找不到pizzapy模塊的錯(cuò)誤。 但是,如果我們把它作為包的一部分來調(diào)用它

$ python3 -m pizzashop.shoppizza.py module name is pizza[, , ]

它能正常工作了。 這到底是怎么回事?

對(duì)此的解釋原因在于Python模塊的搜索路徑,在模塊文檔中有很詳細(xì)的描述。

模塊搜索路徑是解釋器用于查找模塊的目錄(在運(yùn)行時(shí)可用sys.path得到)的列表。 它通過Python標(biāo)準(zhǔn)模塊(/usr/lib64/python3.6)的路徑進(jìn)行初始化,site-packages是pip放置全局安裝的所有內(nèi)容的地方,也是一個(gè)依賴如何運(yùn)行模塊的目錄。 如果將模塊像這樣python3 pizzashop/shop.py作為一個(gè)文件運(yùn)行,則將包含目錄(pizzashop)的路徑添加到sys.path中。 另外,使用-m選項(xiàng)運(yùn)行時(shí),當(dāng)前目錄(如在pwd中)被添加到模塊搜索路徑。 我們可以通過在pizzashop/shop.py中打印sys.path來檢查它:

$ pwd/home/avd/dev/python-imports$ tree.├── pizzapy│ ├── __init__.py│ ├── __main__.py│ ├── menu.py│ └── pizza.py└── pizzashop ├── __init__.py └── shop.py$ python3 pizzashop/shop.py['/home/avd/dev/python-imports/pizzashop', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/local/lib64/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/lib64/python3.6/site-packages', '/usr/lib/python3.6/site-packages']Traceback (most recent call last): File "pizzashop/shop.py", line 5, in import pizzapy.menuModuleNotFoundError: No module named 'pizzapy'$ python3 -m pizzashop.shop['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/local/lib64/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/lib64/python3.6/site-packages', '/usr/lib/python3.6/site-packages']pizza.py module name is pizza[, , ]

正如你在第一種情況中可以看到的,我們?cè)诼窂街杏衟izzashop dir,所以我們找不到兄弟包pizzapy,而在第二種情況下,當(dāng)前dir(表示為"")在sys.path中并且包含兩個(gè)包。

Python的模塊搜索路徑在運(yùn)行時(shí)可作為sys.path

如果將模塊作為腳本文件運(yùn)行,則將包含該模塊的目錄添加到sys.path中,否則,會(huì)將當(dāng)前目錄添加到sys.path中

當(dāng)人們將一堆測(cè)試或示例腳本放在主包相鄰的目錄或包中時(shí),常常會(huì)出現(xiàn)導(dǎo)入同級(jí)包的問題。 這里有幾個(gè)StackOverflow問題:

https://stackoverflow.com/q/6323860

https://stackoverflow.com/q/6670275

好的解決方案是把測(cè)試或例子放在包里,然后使用相對(duì)的導(dǎo)入來避免這個(gè)問題。 差點(diǎn)的解決方案是在運(yùn)行時(shí)修改sys.path,增加所需包的父目錄(耶,動(dòng)態(tài)!)。 人們實(shí)際上這樣做,雖然這是一個(gè)糟糕的方式。

結(jié)束語

我希望閱讀這篇文章之后,你將會(huì)對(duì)Python的導(dǎo)入有更好的理解,并且可以最終順利地將你工具箱中的巨大腳本分解成多個(gè)部分。最后,Python中的所有東西都非常簡單,即使它不能完整地滿足你的需求,你總可以在運(yùn)行時(shí)隨時(shí)修改任何內(nèi)容。

目前想寫的就這些,謝謝你的關(guān)注。 接下來如何,下次分解!

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    626

    瀏覽量

    28965
  • python
    +關(guān)注

    關(guān)注

    53

    文章

    4753

    瀏覽量

    84070

原文標(biāo)題:Python imports指南

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    導(dǎo)入Python庫失敗的缺失庫怎么解決

    在寫 Python 項(xiàng)目的時(shí)候,我們可能經(jīng)常會(huì)遇到導(dǎo)入模塊失敗的錯(cuò)誤:ImportError: No module named xxx或者M(jìn)oduleNotFoundError: No module
    發(fā)表于 11-21 11:46 ?2872次閱讀
    <b class='flag-5'>導(dǎo)入</b><b class='flag-5'>Python</b>庫失敗的缺失庫怎么解決

    python模塊安裝方法

    Python模塊是一個(gè)Python文件,以.py結(jié)尾,包括了Python對(duì)象定義和Python語句,能讓Python代碼段更有邏輯性、
    發(fā)表于 04-04 14:57

    理解python模塊的緩存

    my_mod01$ python my_mod02.pyin mod01該現(xiàn)象的解釋是:因?yàn)?b class='flag-5'>有 sys.modules 的存在。sys.modules 是一個(gè)字典(key:模塊名,value:模塊對(duì)象
    發(fā)表于 03-14 16:42

    Python編程實(shí)用指南

    Python 是一種解釋型、面向?qū)ο蟆?dòng)態(tài)數(shù)據(jù)類型的高級(jí)程序設(shè)計(jì)語言。通過 Python 編程,我們能夠解決現(xiàn)實(shí)生活中的很多任務(wù)。本書是一本面向?qū)嵺`的 Python 編程實(shí)用指南。本書
    發(fā)表于 09-27 06:21

    完全自學(xué)指南Python爬蟲BeautifulSoup詳解

    完全自學(xué)指南Python爬蟲BeautifulSoup詳解
    發(fā)表于 09-07 08:55 ?39次下載
    完全自學(xué)<b class='flag-5'>指南</b><b class='flag-5'>Python</b>爬蟲BeautifulSoup詳解

    Python的Anaconda入門指南

    Python的入門學(xué)習(xí)并不是一件簡單的事情,也不是輕輕松松簡簡單單就可以快速入門的,尤其是環(huán)境問題,讓不少的Python初學(xué)者頭痛不已,本篇文章小編就帶大家看一下Python初學(xué)者的Anaconda
    的頭像 發(fā)表于 01-22 17:32 ?2504次閱讀

    如何使用python將txt文件導(dǎo)入到mysql的應(yīng)用實(shí)例

    實(shí)現(xiàn)思想: 1、python 自動(dòng)完成在txt 文件中加入自定義標(biāo)簽(簡單的txt 文件可以不需要) ,2、python 自動(dòng)完成將含有自定義標(biāo)簽的txt 文件導(dǎo)入到mysql。除了原始txt 文件
    發(fā)表于 09-09 17:50 ?12次下載
    如何使用<b class='flag-5'>python</b>將txt文件<b class='flag-5'>導(dǎo)入</b>到mysql的應(yīng)用實(shí)例

    python包、模塊和庫是什么

    1. 模塊 以 .py 為后綴的文件,我們稱之為 模塊,英文名 Module。 模塊讓你能夠邏輯地組織你的 Python 代碼段,把相關(guān)的代碼分配到一個(gè)模塊里能讓你的代碼更好用,更易懂。 假設(shè)現(xiàn)在
    的頭像 發(fā)表于 03-09 16:47 ?2236次閱讀

    python包模塊相對(duì)導(dǎo)入from和import介紹1

    無包文件**init**.py下,python通過import module導(dǎo)入模塊時(shí),先搜索程序運(yùn)行主目錄。 程序運(yùn)行主目錄為運(yùn)行的py文件所在目錄,而不是執(zhí)行python.exe時(shí)所在目錄。 模塊搜索路徑sys.
    的頭像 發(fā)表于 02-21 14:15 ?948次閱讀

    簡述python包模塊import和from及all

    python指定導(dǎo)入目錄路徑,稱為包導(dǎo)入。 通過import和from導(dǎo)入包模塊。 通過**all**指定導(dǎo)入的模塊和
    的頭像 發(fā)表于 02-21 14:20 ?1152次閱讀

    TSMaster小功能—Python小程序如何導(dǎo)入外部庫

    今天給大家介紹TSMaster功能之Python小程序如何導(dǎo)入外部庫。通過在TSMaster默認(rèn)的解析器路徑下導(dǎo)入外部庫來介紹,以便我們?nèi)ナ褂?b class='flag-5'>Python外部庫。TSMaster默認(rèn)
    的頭像 發(fā)表于 08-14 10:06 ?994次閱讀
    TSMaster小功能—<b class='flag-5'>Python</b>小程序如何<b class='flag-5'>導(dǎo)入</b>外部庫

    Python import Hook 實(shí)現(xiàn)遠(yuǎn)程導(dǎo)入模塊

    的進(jìn)階技能,會(huì)深入地探討并以真實(shí)案例講解 Python import Hook 的知識(shí)點(diǎn)。 當(dāng)然為了使文章更系統(tǒng)、全面,前面會(huì)有小篇幅講解基礎(chǔ)知識(shí)點(diǎn),但請(qǐng)你耐心的往后讀下去,因?yàn)楹竺娌攀潜酒恼碌木A所在,希望你不要錯(cuò)過。 1. 導(dǎo)
    的頭像 發(fā)表于 11-02 11:45 ?532次閱讀
    <b class='flag-5'>Python</b> import Hook 實(shí)現(xiàn)遠(yuǎn)程<b class='flag-5'>導(dǎo)入</b>模塊

    python調(diào)用math函數(shù)的方法

    中。本文將詳細(xì)介紹math模塊中的各種數(shù)學(xué)函數(shù)的調(diào)用方法,包括函數(shù)的功能、參數(shù)的使用和返回值的含義等方面,以幫助讀者更好理解和應(yīng)用這些函數(shù)。 一、導(dǎo)入math模塊 要使用math模塊中的函數(shù),首先需要將其
    的頭像 發(fā)表于 11-22 11:01 ?2096次閱讀

    python如何導(dǎo)入模塊

    Python是一種強(qiáng)大的編程語言,它支持模塊化編程,使得開發(fā)者可以將代碼分解為可重用且獨(dú)立的模塊。模塊是一個(gè)包含函數(shù)、類和變量等定義的文件,我們可以使用import語句將這些模塊導(dǎo)入到我們的程序中
    的頭像 發(fā)表于 11-22 14:46 ?797次閱讀

    Python怎么導(dǎo)入math模板

    用math模塊,首先需要在Python腳本或交互式解釋器中導(dǎo)入它。導(dǎo)入math模塊的方法兩種,分別是導(dǎo)入整個(gè)模塊和只
    的頭像 發(fā)表于 11-22 14:49 ?1478次閱讀