天天看點

Python子產品化Python子產品(Python Module)

Python子產品(Python Module)

一般來說,在程式設計語言中。包、庫、子產品都是指同一個概念,是一種代碼的組織方式,将一種功能或若幹種封裝起來,供自己或他人使用。

Python中隻有一種組織方式,稱為子產品。

module

  • Python中的子產品module,指的是Python源代碼檔案
  • python中的一個類。子產品導入後,會執行個體化一個module對象友善使用

package

  • 包package是一種特殊的module
  • 為了便利子產品的組織,使用檔案夾(目錄)加

    __init__.py

    檔案來作為包,包也是一個子產品,檔案夾中

    __init__.py

    的内容則視為是包的内容。在包中的其他源代碼檔案則為包的子子產品。

import

在python中,可以使用import語句來導入子產品,導入後會生成一個子產品對象綁定到本地的名詞空間中,對象擁有子產品中所有的對象。可以通過點運算符來使用。

python中隻要一個帶有

__init__.py

的檔案夾或者一個

.py

檔案都視為是一個子產品。都可以通過import語句導入。import有以下幾種用法。

測試子產品的檔案結構

m
+--m1
	+--__init__.py
	+--m2
		+--__init__.py
+--__init__.py
+--test.py

#不帶字尾的為包,并且都帶有各自的__init__.py檔案
m - __init__.py
x = 100 在m子產品中有一個屬性x=100
           

導入方式

  • import module
print(dir())
import m

print(dir())
print(type(m))
print(m.x)


#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
# <class 'module'>
#100
           

可以看到導入後本地名詞空間中增加了一個新對象,與子產品名相同。并且可以使用點運算符通路到子產品中的成員。

  • import module as name
print(dir())
import m as alias

print(dir())
print(type(alias))
print(alias.x)

# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'alias']
# <class 'module'>
#100
           

增加到名詞空間中的對象名稱為as後的名稱。使用效果與直接導入相同,原名稱m并不在其中。

  • import package.module
import m.test

print(dir())
print(m.test)

# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
# <module 'm.test' from 'D:\\PythonRoot\\module_test\\m\\test.py'>
           

注意:import隻能導入子產品,即無法通過import module.attribute來導入子產品中的屬性。

這裡可以發現即使使用了

import m.test

,綁定的名詞依舊是m。那麼這樣與直接導入m有什麼差異呢?

import m

print(dir())
print(m.test)

# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
#!AttributeError: module 'm' has no attribute 'test'
           

發現隻導入頂級子產品的話,直接使用頂級子產品下的其他子產品就會引發

AttributeError

屬性異常。不存在test屬性,即test子產品沒有被導入。

import m.test


print(m.test)
print(m.m1)

#<module 'm.test' from 'D:\\PythonRoot\\module_test\\m\\test.py'>
#!AttributeError: module 'm' has no attribute 'm1'
           

使用

import m.test

發現,隻有test子產品被加載了,與test同級的m1沒有被加載

import m.m1.m2


print(m)
print(m.m1)
print(m.m1.m2)


# <module 'm' from 'D:\\PythonRoot\\module_test\\m\\__init__.py'>
# <module 'm.m1' from 'D:\\PythonRoot\\module_test\\m\\m1\\__init__.py'>
# <module 'm.m1.m2' from 'D:\\PythonRoot\\module_test\\m\\m1\\m2\\__init__.py'>
           

導入子子產品時,父子產品也會被導入

  • import package.package.module as name
import m.m1.m2 as alias
  
print(alias)
  
  
#<module 'm.m1.m2' from 'D:\\PythonRoot\\module_test\\m\\m1\\m2\\__init__.py'>
           

可以通過as name的方式,直接将子產品綁定到名詞上,友善使用。

總結

  • 通過import導入子產品後會生成一個子產品對象,可以通過子產品對象通路到其中的對象。
  • import隻能導入子產品
  • 加載子子產品,則一定會加載其父子產品
  • import分兩步,加載和将名詞導入。在加載時,隻會加載顯式導入的子產品,例如

    import m.test

    隻會加載m子產品和test子產品,雖然名詞空間中隻有m,但是可以使用

    m.test

    通路到test子產品。

    import m

    隻會加載m子產品,使用

    m.test

    時則會導緻異常。
  • 在名詞導入時,預設隻會将頂級子產品名導入,使用as則會把子產品綁定到指定的名詞并導入。

from import

在Python中,為了友善使用還提供了另一種導入方式,即

from import

用法

  • from package import module
from m import test

print(dir())

#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test']
           

通過這種方式能直接把test子產品對象的名稱直接導入到目前的名詞空間中,相當于

import m.test as test

,這裡的package可以是非頂級的包。即可以用

import m.m1 import m2

的方式來導入m2子產品。

  • from module import attribute
from m import x

print(dir())
print(x)

# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'x']
# 100
           

可以使用這種方式直接導入子產品中的屬性

  • from import 能夠導入子產品或者屬性,那麼如果在一個子產品下有一個子子產品和其屬性同名呢?

在m的

__init__.py

檔案中加入

test = 'attribute m.test'

from m import test

print(test)

#attribute m.test
           

可以發現導入對象并不是一個子產品,即from import會優先導入非子產品的屬性

總結

  • from import能夠導入子產品或者屬性,并且會将導入的子產品或者屬性直接加入到本地名詞空間中
  • from後必須是一個子產品,語句無法從其他對象導入屬性
  • 如果有同名的屬性和子產品,會優先導入非子產品屬性

子產品對象屬性

在Python3之中,萬物皆對象,module也是一個對象,可以通過其

__dict__

來檢視其屬性。

import m

print(m.__dict__.keys())

#dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'test'])
           

發現除了x和test自己添加的屬性外,預設還帶有許多屬性。

變量名 類型 作用

__name__

str 子產品名

__doc__

str 文檔字元串

__package__

str 所在的包名,如果是包,則是自身名稱

__spec__

_frozen_importlib.ModuleSpec類執行個體

子產品使用的标準

__path__

list 包的檔案夾所在的路徑。隻有包有該屬性。

__file__

str 檔案的路徑。如果是包,則是

__init__.py

檔案的 路徑。

__cached__

str 記錄子產品編譯後生成

.pyc

檔案的位置

查找順序和加載方式

Python中使用一個list來存儲搜尋子產品時的路徑,優先級從低到高依次降低。從索引0開始查找,隻要找到了就停止,找不到傳回importError異常。

sys.path

sys.path

中存放了查找子產品時所用到的路徑。是一個有序的可寫的list,可以使用list操作往其中增加或删減路徑。

注意:

sys.path

必須存放絕對路徑

sys.module

在m子產品中加入列印語句

print(__name__)

import m
import m

#m
           

可以發現列印語句隻執行了一次,說明在Python中存在一個導入機制,不會導入重複的子產品。

Python将導入的子產品放到了

sys.module

,是一個dict類型。解釋器在運作時,發現要加載的子產品已經加載過了,就隻使用一個名稱來綁定這個子產品,不再重新導入。我們可以也可以使用這個字典直接來通路已經加載過的子產品。

主子產品

  • 程式中的第一個入口,其他子產品皆為該子產品直接或間接加載
  • 運作的第一個

    .py

    檔案
  • __name__

    屬性名為

    '__main__'

if __name__ == '__main__':
    pass
           

利用這一特性,可以用上面文法來寫子產品的測試語句。隻有當此子產品為主子產品時才會執行其中語句。

絕對導入和相對導入

絕對導入

文章至此,使用的都是絕對導入,使用一個子產品名,從

sys.path

中搜尋路徑導入的方式都是絕對導入。

相對導入

  • 相對導入隻能在

    from import

    中使用
  • 隻能在一個包内使用
  • 使用相對導入後的子產品無法作為主子產品來使用
用法

與絕對路徑相似,但在from後使用相對路徑

符号 含義

.

目前目錄

..

上級目錄

...

上上級目錄

比如可以在m2中使用

from ... import test

來通路m下的test子產品,也可以使用

from ..package import module

來導入同級目錄包内的子產品,

from .module import attribute

導入同級目錄下子產品屬性。

需要注意的是,如果是包,是以

__init__.py

檔案位置為準,并非檔案夾。

from import *

  • from import

    可以使用

    from module import *

    的方式來簡易得導入多個屬性
  • 預設隻會導入非下劃線開頭的變量名,不會導入子產品,如果導入的子產品中顯式地導入了其他子產品,且導入子產品使用的變量名非下劃線開頭,該子產品對象也會被導入。
  • __all__

    屬性
    • 子產品預設沒有定義

      __all__

      屬性,當沒有該屬性時,

      from import *

      隻會導入非下劃線開頭以及非子產品屬性。
    • __all__

      是一個可疊代對象,常用tuple和list
    • 如果定義了

      __all__

      from import *

      隻會導入其中的變量,并且不管是否是子產品(這裡的子產品指的是未被加載的子子產品)或是下劃線開頭。