模塊間相互獨立相互引用是任何一種編程語言的基礎能力。對於“模塊”這個詞在各種編程語言中或許是不同的,但我們可以簡單認為一個程序文件是一個模塊,文件里包含了類或者方法的定義。對於編譯型的語言,比如C#中的一個.cs文件,Java中的一個.java或者編譯后的.class文件可以認為是一個模塊(但常常不表述為模塊);對於解釋型的語言會更加直觀些,比如PHP的.php文件,在Python中就是.py文件可以認為是一個模塊。在“模塊”之上有“包”,主要是為了方便組織和管理模塊。比如C#中編譯后的.dll文件(但常常不表述為包Package,而是庫Library),Java將.class打包后的.jar文件,PHP的.phar文件(模仿Java包),在Python中一個特殊定義的文件夾是一個包,可以打包為egg文件。但對於解釋型語言“包”並沒有編譯成低級語言而后打包的意思,只是更加方便模塊化和管理模塊間的依賴。每種編程語言對於模塊和包管理都有一定的約定,不了解這些約定,那會給學習這種語言的帶來障礙。下面我想來梳理一下Python的這些約定。
一、Python查找模塊的路徑
運行Python應用或引用Python模塊,Python解釋器要有一個查找的過程。可以通過設置一個環境變量PYTHONPATH為Python增加一個搜索路徑,以方便查找到相關Python模塊(不同的操作系統環境變量的設置稍有不同,默認以下都是WIndows環境),這與眾多應用程序需要設置一個系統環境變量的道理是一樣的。在命令行中可以通過以下命令設置:
C:\Users\Administrator>set PYTHONPATH=E:/Project/Python/ModuleAndPackage/
進入Python環境后可以,通過Python的sys.path屬性獲得當前搜索路徑的配置,可以看到之前我們設置的路徑已經在當前搜索路徑中了。
1
2
3
4
5
6
7
|
C:\Users\Administrator>python
Python
2.7
.
11
(v2.
7.11
:
6d1b6a68f775
, Dec
5
2015
,
20
:
32
:
19
) [MSC v.
1500
32
bit (Intel)] on win32
Type
"help"
,
"copyright"
,
"credits"
or
"license"
for
more information.
>>>
import
sys
>>> sys.path
['
', '
E:\\Project\\Python\\ModuleAndPackage
', '
C:\\Windows\\system32\\python27.
zip
', '
C:\\Python\\DLLs
', '
C:\\Python\\lib
', '
C:\\Python\\lib\\plat
-
win
', '
C:\\Python\\lib\\lib
-
tk
', '
C:\\Python
', '
C:\\Python\\lib\\site
-
packages']
>>>
|
也可以通過sys模塊的append方法在Python環境中增加搜索路徑。
1
2
3
4
|
>>> sys.path.append(
"E:\\Project\\Python\\ModuleAndPackage2"
)
>>> sys.path
['
', '
E:\\Project\\Python\\ModuleAndPackage
', '
C:\\Windows\\system32\\python27.
zip
', '
C:\\Python\\DLLs
', '
C:\\Python\\lib
', '
C:\\Python\\lib\\plat
-
win
', '
C:\\Python\\lib\\lib
-
tk
', '
C:\\Python
', '
C:\\Python\\lib\\site
-
packages
', '
E:\\Project\\Python\\ModuleAndPackage2']
>>>
|
二、Python中的模塊和包
前面已經提到每個.py文件都是可以認為是一個Python模塊,.py文件中可以包含類、方法、變量和常量(Python還沒有嚴格意義上的常量,只是約定大寫的變量作為常量),文件內也可以直接寫所有的邏輯語句並在加載時從上之下直接執行,這與其他解釋型語言是類似的。例如我們選擇在文件夾ModuleAndPackage中創建一個文本文件person.py文件即創建了一個簡單的Python模塊,其內容如下:
1
2
3
4
5
|
# -*- coding: utf-8 -*-
ID
=
1
name
=
"This person"
print
namedef say(something):
print
name,
'says'
, something
|
那么接下來我們就可以在Python環境中執行person.py。我們可以直接像執行一個批處理文件那樣執行person.py,在cmd命令行輸入:
1
|
Python E:
/
Project
/
Python
/
ModuleAndPackage
/
person.py
|
本質上任何一個Python應用的入口模塊都是這樣被執行的(像C#和Java中的main函數),但是引用一個模塊,就要建立運行它的上下文環境。我們先設置一個環境變量PYTHONPATH,以便Python解釋器找到person.py模塊,然后import person模塊,即可訪問其中的方法或變量。
1
2
3
4
5
6
7
8
|
C:\Users\Administrator>python
Python
2.7
.
11
(v2.
7.11
:
6d1b6a68f775
, Dec
5
2015
,
20
:
32
:
19
) [MSC v.
1500
32
bit (
Intel)] on win32
Type
"help"
,
"copyright"
,
"credits"
or
"license"
for
more information.
>>>
import
person
This person>>> person.say(
"hello"
)
This person says hello
>>>
print
person.nameThis person>>>
|
Python需要去某些固定的路徑下去查找Python模塊,上面我們設置在ModuleAndPackage中查找。但是這些路徑下也是有目錄層次的,Python是如何查找子目錄中的模塊呢?特別是引用第三方包時,我們也需要知道一定的層次關系。實際上,Python通過目錄和文件構建包結構,並且包是層層嵌套的,和目錄層層嵌套是一樣的,這樣就構成了包內的訪問路徑(或者命名空間,也可以說Python應用的命名空間與其目錄和文件結構是對應了,似乎缺少了一些靈活,但也更簡單)。例如我們在ModuleAndPackage文件夾下,創建一個文件夾animal,里面創建一個文本文件pet.py,其內容如下:
1
2
3
4
5
|
# -*- coding: utf-8 -*-
ID
=
2
name
=
"This pet"
print
namedef run(somewhere):
print
name,
'runs'
, somewhere
|
那么如何引用pet.py這個模塊呢?按照Python的約定,需要在animal文件夾中創建名為__init__.py的空文本文件,以標識animal文件夾是一個包。倘若animal文件夾內還有文件夾作為包,也必須包含__init__.py文件。這樣就層層標識了訪問的路徑。
1
2
3
|
>>>
import
animal.pet
This pet>>>
print
animal.pet.name
This pet>>> animal.pet.run(
"everywhere"
)This pet runs everywhere>>>
|
或者使用from關鍵字直接導入模塊內的屬性或方法:
1
2
3
4
5
|
>>>
from
animal.pet
import
name,run
>>>
print
name
This pet>>> run(
"everywhere"
)
This pet runs everywhere
>>>
|
三、Python模塊間引用
簡答來說,只要Python模塊在其執行環境配置的搜索路徑中,並且其所在位置是包結構的一部分,那么我們就可以引用該模塊。上文已經提供了模塊引用的基本示例。只不過模塊間引用時import語句是寫在模塊文件中,我們修改person.py模塊的代碼。
1、from、import和as
1
2
3
4
5
6
7
8
9
|
# -*- coding: utf-8 -*-
ID
=
1
name
=
"This person"
print
name
def
say(something):
print
name,
'says'
, something
from
animal.pet
import
name as pet_name, run as pet_run
def
have():
print
name,
'has'
, pet_name
|
import語句可以寫在文檔中的任何位置,甚至if語句中,以便更好的控制模塊引用。還可以通過as語句,使用另一個變量名進行引用,以避免變量名沖突。
1
2
3
4
5
6
7
8
9
10
|
>>>
import
person
This person
This pet
>>>
print
person.name
This person
>>>
print
person.pet_name
This pet
>>> person.have()
This person has This pet
>>>
|
2、*通配符
上面的import代碼明確了引用的變量名,但如果想引用模塊中所有變量可以使用*通配符,將上面的import語句改寫如下:
1
|
from
animal.pet
import
*
|
但這樣有可能造成變量名沖突,如下name變量發生沖突,覆蓋了person自己的name變量的值:
1
2
3
4
5
|
>>>
import
person
This person
This pet
>>>
print
person.name
This pet
|
但如果想用*通配符,又不想引用模塊中的所有變量,可以在模塊中用變量__all__進行限制,修改pet.py,限制只引用ID和run兩個變量名。
1
2
3
4
5
6
7
|
# -*- coding: utf-8 -*-
__all__
=
[
'ID'
,
'run'
]
ID
=
2
name
=
"This pet"
print
name
def
run(somewhere):
print
name,
'runs'
, somewhere
|
因為沒有引用pet模塊中的name變量,person的name變量值沒有改變,run卻可以調用了。
1
2
3
4
5
6
7
8
|
>>>
import
person
This person
This pet
>>>
print
person.name
This person
>>> person.run(
"nowhere"
)
This pet runs nowhere
>>>
|
3、引用包
上面都是引用具體的animal.pet模塊,但是這對於一個相對獨立且擁有眾多的模塊的包來說就顯得麻煩了,可以直接import animal嗎?答案是肯定的,但是Python不像C#引用dll或者java引用jar那樣,引用后包內的模塊就可以通過命名空間直接訪問了(在訪問控制許可下)。默認情況下Python還是需要導入包內的具體模塊的,但有個變通的辦法,就是使用包中__init__.py文件,提前准備包內需要被引用的各個模塊中的變量,類似於向外部引用者暴露包內接口。__init__.py文件代碼是在包或者包內模塊被引用時執行的,因而可以在其中做一些初始化的工作。修改animal文件夾中__init__.py文件如下,其中模塊可以使用絕對路徑和相對路徑,相對路徑中一個句點.代表同級目錄,兩個句點..代表父目錄。
1
2
|
print
"__init__"
from
pet
import
name as pet_name, run as pet_run
#from animal.pet import name as pet_name, run as pet_run #from .pet import name as pet_name, run as pet_run
|
修改person.py,直接引用anmial包:
1
2
3
4
5
6
7
8
9
|
# -*- coding: utf-8 -*-
ID
=
1
name
=
"This person"
print
name
def
say(something):
print
name,
'says'
, something
import
animal
def
have():
print
name,
'has'
, pet_name
|
在Python環境中引用person模塊,person引用animal,並自動執行__init__的代碼加載相關變量,通過dir方法可以查看模塊中的變量,其中兩個下划線開始的變量每個模塊都有,這些變量具有特殊的作用,是Python預定義的。
1
2
3
4
5
6
7
8
9
10
11
12
|
>>>
import
person
This person
__init__
This pet
>>>
dir
(person)
[
'ID'
,
'__builtins__'
,
'__doc__'
,
'__file__'
,
'__name__'
,
'__package__'
,
'have'
,
'name'
,
'pet'
,
'pet_name'
,
'pet_run'
,
'say'
]
>>>
print
person.pet_name
This pet
>>> person.pet_run(
"nowhere"
)
This pet runs nowhere
>>>
|
關於Python引用模塊和Python查找模塊路徑的相關知識,小編就給大家介紹這么多,希望對大家有所幫助!