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