1. python程序由包(package)、模塊(module)和函數組成。
2. 包是由一系列模塊組成的集合。當不同作的模塊進(jìn)行按文件夾分類(lèi)后再組成一個(gè)整體的庫,可以稱(chēng)為包。為了讓Python將目錄當做內容包,目錄中必須包含__init__.py文件,用于標識當前文件夾是一個(gè)包。最簡(jiǎn)單的情況下,只需要一個(gè)空的__init__.py文件即可。包就是一個(gè)完成特定任務(wù)的工具箱,包的作用是實(shí)現程序的重用。包導入會(huì )讓模塊扮演的角色更為明顯,也使代碼更具有可讀性。
3. 模塊是處理某一類(lèi)問(wèn)題的函數和類(lèi)的集合,由代碼、函數和類(lèi)組成。函數是一段可以重復多次調用的代碼。模塊把一組相關(guān)的函數或代碼組織到一個(gè)文件中,一個(gè)文件即是一個(gè)模塊。每個(gè)模塊文件是一個(gè)獨立完備的命名空間,一個(gè)模塊文件不能看到其他文件定義的變量名,除非它明確地導入了那個(gè)文件,模塊文件起到了最小化命名沖突的作用。
導入模塊使用import和from語(yǔ)句(都是隱性的賦值語(yǔ)句),以及reload函數??梢詫肽K名,還可以指定目錄路徑(Python代碼的目錄就稱(chēng)為包),包導入就是把計算機上的目錄變成另一個(gè)Python命名空間,包的屬性就是該目錄包含的子目錄和模塊文件。當多個(gè)同名程序文件安裝在某機器上時(shí),包導入可以偶爾用來(lái)解決導入的不確定性。導入包也使用import和from語(yǔ)句。
import file_name可以導入當前目錄上file_name.py這個(gè)模塊,但是里面的內容,必須通過(guò)file_name.func/file_name.var 來(lái)訪(fǎng)問(wèn)。
如果你一直在某個(gè)環(huán)境,比如解釋器下面,你已經(jīng)導入過(guò)某個(gè)模塊 ,現在你對模塊進(jìn)行了修改,這里你需要用reload(modulename)來(lái)重新載入。
Python里,多次import的效果是只有第一次import有用,如果想要重新載入模塊應該用reload(your_module)。
局部import時(shí)還可以使用這種語(yǔ)法
__import__('shutil').rmtree(DATA_DIR)
subpackage導入時(shí)要這樣:
__import__('geopy.distance').distance.vincenty(i, j).miles
相當于import geopy.distance; distance.vincenty(i, j).miles?
另外一種導入方式為import func/var from file_name。這樣func/var直接可用,但是file_name是沒(méi)有定義的。
從file_name中導入所有:import * from file_name。這樣會(huì )導入所有除了以下劃線(xiàn)開(kāi)頭的命名。實(shí)際代碼中這樣做往往是不被鼓勵的。
1. 與import類(lèi)似, 被導入的module仍然會(huì )執行且僅執行一次
2. from *** import 的實(shí)質(zhì)
當以 "from *** import " 方式導入module時(shí), python會(huì )在當前module 的命名空間中新建相應的命名.
即, "from t2 import var1" 相當于:
import t2
var1= t2.var1
在此過(guò)程中有一個(gè)隱含的賦值的過(guò)程
由于python賦值操作特性(參考python變量名本質(zhì)),在當前代碼中對"var1"的修改并不能影響到t2.py中"var1"的值. 同時(shí), 在t2.py中對var1的修改也不能反映到當前的代碼.
from package import item # 這種方式,item可以是包中的一個(gè)子模塊或子包,也可以是包中定義的其他命名,像函數、類(lèi)、變量。import item.subitem.subsubitem # 這些子項必須是包,最后的子項是包或模塊。但不能為函數、類(lèi)或變量。否則出錯:No module named ***Note:
1. 陷阱:使用from導入變量,而那些變量碰巧和作用域中現有的變量同名,本地變量就會(huì )被悄悄地覆蓋掉;使用import則沒(méi)這個(gè)問(wèn)題。
2.當b.py文件以from filename import fun方式從a.py文件引入函數fun時(shí),如果有可執行語(yǔ)句在函數fun外,則引入后,執行b.py也會(huì )同時(shí)執行a.py在函數外的可執行語(yǔ)句,故一般不要把可執行腳本直接寫(xiě)到函數外,實(shí)在不想寫(xiě)入函數中也可以使用if __name__ == '__main__':...的方式。
首先會(huì )搜索解析器的當前目錄。然后會(huì )到sys.path變量中給出的目錄列表中查找。
>>> import sys>>> sys.path['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']PYTHONPATH表示的目錄列表中搜索Note:sys.path包含輸入模塊的目錄名列表。我們可以觀(guān)察到sys.path的第一個(gè)字符串是空的——這個(gè)空的字符串表示當前目錄也是sys.path的一部分,這與PYTHONPATH環(huán)境變量是相同的。這意味著(zhù)你可以直接輸入位于當前目錄的模塊。否則,你得把你的模塊放在sys.path所列的目錄之一。[python系統模塊sys、os和路徑、系統命令]
當用戶(hù)寫(xiě)下 from sound.Effects import * 時(shí)會(huì )發(fā)生什么事?理想中,總是希望在文件系統中找出包中所有的子模塊,然后導入它們。這可能會(huì )花掉很有長(cháng)時(shí)間,并且出現期待之外的邊界效應,導出了希望只能顯式導入的包。
對于包的作者來(lái)說(shuō)唯一的解決方案就是給提供一個(gè)明確的包索引。
執行 from package import *時(shí),如果包中的__init__.py 代碼定義了一個(gè)名為__all__ 的列表,就會(huì )按照列表中給出的模塊名進(jìn)行導入。新版本的包發(fā)布時(shí)作者可以任意更新這個(gè)列表。如果包作者不想 import * 的時(shí)候導入他們的包中所有模塊,那么也可能會(huì )決定不支持它( import * )。例如,sounds/effects/__init__.py 這個(gè)文件可能包括如下代碼:
__all__ = ["echo", "surround", "reverse"]這意味著(zhù) from Sound.Effects import * 語(yǔ)句會(huì )從 sound 包中導入以上三個(gè)已命名的子模塊。
如果沒(méi)有定義 __all__ , from Sound.Effects import * 語(yǔ)句 不會(huì ) 從 sound.effects 包中導入所有的子模塊。無(wú)論包中定義多少命名,只能確定的是導入了 sound.effects 包(可能會(huì )運行__init__.py 中的初始化代碼)以及包中定義的所有命名會(huì )隨之導入。這樣就從__init__.py 中導入了每一個(gè)命名(以及明確導入的子模塊)。同樣也包括了前述的 import 語(yǔ)句從包中明確導入的子模塊。
考慮以下代碼:
import sound.effects.echoimport sound.effects.surroundfrom sound.effects import *在這個(gè)例子中,echo 和 surround 模塊導入了當前的命名空間,這是因為執行 from...import 語(yǔ)句時(shí)它們已經(jīng)定義在 sound.effects 包中了(定義了__all__時(shí)也會(huì )同樣工作)。
盡管某些模塊設計為使用 import * 時(shí)它只導出符全某種模式的命名,仍然不建議在生產(chǎn)代碼中使用這種寫(xiě)法。
記住,from Package import specific_submodule 沒(méi)有錯誤!事實(shí)上,除非導入的模塊需要使用其它包中的同名子模塊,否則這是推薦的寫(xiě)法。
很多情況下導入子包會(huì )導致和真正的標準庫模塊發(fā)生(事實(shí)上是它們的名字)沖突。 包模塊會(huì )把名字相同的標準庫模塊隱藏掉, 因為它首先在包內執行相對導入, 隱藏掉標準庫模塊。
從 Python 2.7 開(kāi)始默認絕對導入。 python3.x 中的包內的模塊導入原則編程了:如果要相對導入,那么一定是顯式聲明,并且,這個(gè)聲明不是告訴python一個(gè)模塊查找偏好,而是一種約束(也就是說(shuō),一旦聲明,必須是相對導入)
from __future__ import absolute_import: 在 3.0 以前的舊版本中啟用相對導入等特性所必須的 future 語(yǔ)句。
Python3絕對導入:按照sys.path順序搜索,先主目錄(sys.path中第一項''),然后PYTHONPATH環(huán)境變量、標準庫路徑、pth指定路徑等。
import 語(yǔ)句總是絕對導入的, 所以相對導入只應用于 from-import 語(yǔ)句。
Python3相對導入:在模塊所在同一個(gè)包內搜索,注意該包目錄與主目錄的區別。Python3中,每一個(gè)點(diǎn)號代替上一層目錄。這樣做的目的,主要是防止覆蓋命名空間。
Note:從當前目錄相對導入可以省略.號,不過(guò)不省略更好,以免之后修改系統不會(huì )自動(dòng)調整(使用pycharm時(shí),移動(dòng)文件時(shí),有.號它會(huì )自動(dòng)修改導入路徑)。
Example
Phone/
__init__.py
common_util.py #其中包含setup
Fax/
__init__.py
G3.py #其中包含dial
Mobile/
__init__.py
Analog.py #其中包含dial
Digital.py
在 Mobile.Analog.Digital , 也就是 Digital.py 模塊中, 不能簡(jiǎn)單地使用錯誤語(yǔ)法(只能工作在舊版本的 Python 下, 在新的版本中它會(huì )導致一個(gè)警告, 或者干脆不能工作)
import Analog
from Analog import dial
正確絕對導入方法:
from Phone.Mobile.Analog import dial正確相對導入方法:
from .Analog import dial # . 代表當前.py文件所在目錄Mobile/
from ..common_util import setup
from ..Fax import G3.dial.
lz總結的一個(gè)模塊導入法則:Use relative import only in modules and run the scripts outside the package. 也就是說(shuō)使用相對導入的文件不應該是要運行執行的文件,而應該只是外層調用的模塊。但是如果你非要在packges里面做測試的話(huà),那就不要使用相對導入。Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.[documentation]
import sysimport os#將mymodule模塊(目錄)所在的目錄(如這個(gè)py文件的../..)加入到pythonpath中就可以使用from mymodule import *了
sys.path.append(os.path.join(os.path.split(os.path.realpath(__file__))[0],"../.."))try:
示例
Note: 最頂層的SocialNetworks是一個(gè)單純的目錄,而下層的SocialNetworks開(kāi)始就是一個(gè)python package.
運行MainTest.py,EBM中導入的就是try中的import
直接運行EBM.py,是從except中import的
也就是使用
try: from .mymodule import myclassexcept Exception: #ImportError from mymodule import myclass來(lái)區分包內和包外的運行測試。
[SystemError: Parent module '' not loaded, cannot perform relative import]
后來(lái)lz發(fā)現pycharm中不用加入路徑就可以導入except中的語(yǔ)句,from SocialNetworks.SocialNetworks引入成功的原因是lz在pycharm中添加了add content roots to pythonpath了,這樣sys.path中多了一個(gè)整個(gè)項目project的路徑/media/pika/files/mine/python_workspace,里面的目錄就被當成包使用,所以可以引入成功!在代碼中加入路徑后,在pycharm之外也可以正常運行from SocialNetworks.SocialNetworks了,所以lz建議關(guān)閉pycharm的add roots to pythonpath這種功能。
[Why does PyCharm always add “contents root” to my PYTHONPATH, and what PYTHONPATH?]
下面這幾個(gè)錯誤好像都可以使用上述的方法解決,下面是以前總結的解決方案,不怎么好。
>>> import sys
>>> sys.path
['', '/home/pipi/ENV/ubuntu_env/lib/python35.zip', '/home/pipi/ENV/ubuntu_env/lib/python3.5', '/home/pipi/ENV/ubuntu_env/lib/python3.5/plat-x86_64-linux-gnu', '/home/pipi/ENV/ubuntu_env/lib/python3.5/lib-dynload', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/home/pipi/ENV/ubuntu_env/lib/python3.5/site-packages']
可以看到/home/pipi/ENV/ubuntu_env/lib/python3.5/site-packages在sys.path中,所以可以在python中調用這些不同的拓展包(如使用pip安裝的包)。
案例1:
目錄樹(shù)
case1/
├── cat
│ ├── __init__.py
│ ├── cat.py
├── dog
│ ├── __init__.py
│ └── dog.py
├── __init__.py
└── main.py
代碼
# case1/cat/cat.py
from .. import dog
# case1/main.py
import cat.cat
執行
python case1/main.py
錯誤原因:
這里的 case1 是一個(gè)包,但當你直接執行 main.py 的時(shí)候,就沒(méi)有把完整的 case1 當作一個(gè)包來(lái)處理了( the package should be entirely self contained. It won't treat case1/ as a package when you're running main.py inside it.),而cat和dog還是包,并且已經(jīng)是包樹(shù)的頂層了??上攵?,下層的 cat.py (import cat.cat)自然找不到上層包了,想要相對導入成功,必須讓下層的被導入對象是上層包或上層包內的對象。
所以使用相對導入的目錄的平級目錄內的所有py文件不允許單獨執行(當然局部測試時(shí)是可以的,而不是將包中的py文件當作主應用程序執行),要保證這個(gè)模塊不是入口文件(py文件中有if __name__ == '__main__',其實(shí)也不一定有這個(gè),只要你執行就算是),只是作為被導入的模塊才可以以這樣使用。
python 中只能在package中使用相對導入,不能在用戶(hù)的應用程序中使用相對導入,因為不論是相對導入還是絕對導入,都是相當于當前模塊來(lái)說(shuō)的,對于用戶(hù)的主應用程序,也就是入口文件,模塊名總是為“ __main__ ”, 所以用戶(hù)的應用程序(如NLP目錄下有一個(gè)main.py[包含__main__],要執行的)必須使用絕對導入,而package(如__main__調用的包NLP\TargetOpinion)中的導入可以使用相對導入。
When launching a python source file, it is forbidden to import another file, that is in the current package, using relative import.
解決:將主程序移出到package外,如整個(gè)python目錄NLP\TargetOpinion\***,TargetOpinion中的packages可以互相相對導入,但是TargetOpinion目錄下不能有main模塊,要移到外面,如NLP目錄下,與TargetOpinion平齊。
當然這個(gè)案例就是將man.py放到case1平齊的目錄中,如case1_1。
報錯2:SystemError: Parent module '' not loaded, cannot perform relative import?
這種情況一般是因為case1不是包(目錄下沒(méi)有__init__),而 cat.cat.py要調用dog.dog.py(from ..dog import dog),這樣上層..不是包不能導入出錯。
不過(guò)在pycharm中設置source root(或者加入../..到sys.path中)好像也可以解決。
案例2:
目錄樹(shù)
case3/
├── alpaca.py
├── main.py
└── pets
├── __init__.py
├── cat
│ ├── __init__.py
│ └── cat.py
└── dog
├── __init__.py
└── dog.py
代碼
# case3/pets/cat/cat.py
from ..dog import dog
from .. import dog
# case3/main.py
from pets.cat import cat
執行
python case3/main.py
請注意,這里的 cat.py 中是不能導入 alpaca 的,因為 pets 已經(jīng)是這個(gè)包樹(shù)的頂層了(case3中包含main.py其中包含main函數,這樣case3就不會(huì )被當作包?)。
2.ImportError: No module named '***'
sys.path沒(méi)有加入導入的模塊所在目錄
[python package module import 完全總結]
在子模塊中你時(shí)常見(jiàn)到的一個(gè)簡(jiǎn)單錯誤,就是使用軟件包的名字來(lái)導入軟件包。
# within a sub-modulefrom a_package import APackageError這樣做會(huì )導致兩個(gè)不好的結果:
盡管第一條看上去并不是什么大問(wèn)題,但是考慮一下,如果你在 PYTHONPATH 下的兩個(gè)目錄中,有兩個(gè)同名的軟件包。你的子模塊可能最終導入了另一個(gè)軟件包,你將無(wú)意間使得某個(gè)或某些對此毫無(wú)戒備的程序員(或是你自己)debug 到深夜。
所以說(shuō),在軟件包中最好只進(jìn)行相對導入,而運行的執行文件(__main__)都放在軟件包外面。
如import scipy或者import dateutil后
使用scipy.misc或者dateutil.parser出錯
原因:because the parser.py is a module in the dateutil package. It's a separate file in the folder structure.所以要手動(dòng)添加。其實(shí)主要是ubuntu_env/lib/python3.5/site-packages/dateutil$ vi __init__.py中沒(méi)有from . import subpackages,自己修改一下就好了,不過(guò)它本身沒(méi)這么加肯定有它的原因,lz暫時(shí)還沒(méi)搞清楚。
解決:所以最好還是使用import dateutil.parser或者from dateutil import parser來(lái)導入模塊。
你的模塊應當比較小。記住,那個(gè)使用你軟件包的程序員會(huì )在軟件包作用域進(jìn)行導入,同時(shí)你會(huì )使用你的 __init__.py 文件來(lái)作為一個(gè)組織工具,來(lái)暴露一個(gè)完整的接口。
好的做法是一個(gè)模塊只定義一個(gè)類(lèi),伴隨一些幫助方法和工廠(chǎng)方法來(lái)協(xié)助建立這個(gè)模塊。
class APackageClass(object): '''One class'''def apackage_builder(how_many): foriinrange(how_many): yieldAPackageClass()如果你的模塊暴露了一些方法,把一些相互依賴(lài)的方法分為一組放進(jìn)一個(gè)模塊,并且把不相互依賴(lài)的方法移動(dòng)到單獨的模塊中,一個(gè)例子是 fsq/enqueue.py,它暴露了一系列的方法來(lái)為同一個(gè)功能提供不同的接口(就像 simplejson 中的l oad/loads)。盡管這個(gè)例子足夠直觀(guān),讓你的模塊保持較小規模需要一些判斷,但是一個(gè)好的原則是:當你有疑問(wèn)的時(shí)候,就去創(chuàng )建一個(gè)新的子模塊吧。
輸入一個(gè)模塊相對來(lái)說(shuō)是一個(gè)比較費時(shí)的事情,所以Python做了一些技巧,以便使輸入模塊更加快一些。
一種方法是創(chuàng )建 字節編譯的文件 ,這些文件以.pyc作為擴展名。字節編譯的文件與Python變換程序的中間狀態(tài)有關(guān)。{一個(gè)用編譯性語(yǔ)言比如C或C++寫(xiě)的程序可以從源文件(即C或C++語(yǔ)言)轉換到一個(gè)你的計算機使用的語(yǔ)言(二進(jìn)制代碼,即0和1)。這個(gè)過(guò)程通過(guò)編譯器和不同的標記、選項完成。當你運行你的程序的時(shí)候,連接/轉載器軟件把你的程序從硬盤(pán)復制到內存中并且運行。而Python語(yǔ)言寫(xiě)的程序不需要編譯成二進(jìn)制代碼。你可以直接從源代碼運行 程序。在計算機內部,Python解釋器把源代碼轉換成稱(chēng)為字節碼的中間形式,然后再把它翻譯成計算機使用的機器語(yǔ)言并運行。}
當你在下次從別的程序輸入這個(gè)模塊的時(shí)候,.pyc文件是十分有用的——它會(huì )快得多,因為一部分輸入模塊所需的處理已經(jīng)完成了。另外,這些字節編譯的文件也是與平臺無(wú)關(guān)的。
dir()函數當你為dir()提供一個(gè)模塊名的時(shí)候,它返回模塊定義的名稱(chēng)列表(函數、類(lèi)和變量)。如果不提供參數,它返回當前模塊中定義的名稱(chēng)列表(當前解釋器中定義的命名)。
>>> import fibo>>> dir(fibo)['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fib', 'fib2']>>> dir()['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'fibo']dir()并不會(huì )列出內置函數和變量名。如果你想列出這些內容,它們在標準模塊__builtin__中定義。
curModuleDir=dir() # get dir of current file(module)同時(shí)這句話(huà)放在模塊(.py)文件函數或類(lèi)外面
修改dir函數表現
class Shape(object):
def__dir__(self): return['area','perimeter','location']模塊的__name__屬性
當一個(gè)模塊被第一次輸入import的時(shí)候,這個(gè)模塊的主塊將被運行。假如我們只想在程序本身被使用的時(shí)候運行主塊,而在它被別的模塊輸入的時(shí)候不運行主塊,這可以通過(guò)模塊的__name__屬性完成。
每個(gè)Python模塊都有它的__name__,如果它是'__main__',這說(shuō)明這個(gè)模塊被用戶(hù)單獨運行,我們可以進(jìn)行相應的恰當操作。
from:http://blog.csdn.net/pipisorry/article/details/43313197
構建健壯 Python 包的 5 個(gè)簡(jiǎn)單規則
聯(lián)系客服