Python 中的 __init__.py 和__all__ 詳解(抄襲的joker) 因爲寫的實在是太好了

Python 中的 __init__.py 和__all__ 詳解

 

之前不論是自己寫代碼還是用別人的代碼,都沒有注意過這個東西,今天忽然看了一下,網上的教程感覺講的都不是很清楚,自己又研究了研究,總結一下,如果有不對的地方,大家幫忙指正一下。

在Python工程裏,當python檢測到一個目錄下存在__init__.py文件時,python就會把它當成一個模塊(module)。個人習慣說這是一個包,只有.py的文件我才說是模塊,不知道我這個表述規範不規範,不規範的話大家幫忙指正一下。

__init__.py這個東西不是必須有的,如果有的話,在調用包的時候,會運行這個文件。沒有的話,也不耽誤。

__init__.py存在的意義,是簡化代碼

看這個示例:

# my_test.py

from test2.sub4.test41 import print_41 # print_41是在test41.py裏定義的函數 後面的都可以依次類推 我就不寫了

print_41()

跑的通,沒任何問題

這樣也跑的通:

from test2.sub4 import test41
test41.print_41()

但這個,是跑不通的,不是把py文件寫在了一個文件夾下,這個文件夾就是知道他下面有啥了的!!!!

from test2 import sub4
sub4.test41.print_41()

# 報錯:AttributeError: module 'test2.sub4' has no attribute 'test41'

下面調整一下,加上__init__.py,但是__init__.py裏什麼都不寫

再跑這段代碼:

from test2 import sub4
sub4.test41.print_41()

不好意思,這樣照樣也是跑不通,會報同樣的錯誤,回到我前面說的,在調用包的時候,會運行__init__.py這個文件,你__init__裏啥也沒寫,就跟沒有一樣

那要怎麼樣這段代碼才能跑的通呢?在兩個__init__文件裏分別寫上這些東西:

# test2/__init__.py
from . import sub4

# test2/sub4/__init__.py
from . import test41

然後運行那一段代碼,就可以跑的通了,原理就是,我在運行 from test2 import sub4 這句代碼的時候,就運行了 test2/sub4/__init__.py (其實test2/__init__.py也有運行) 就相當於在sub4這個包裏,引入了test41這個模塊,sub4知道自己有這個模塊了。

這裏還有一個,我爲什麼要寫 from . import test41 直接 import 不行嗎?不行的,在我們執行 import 時,當前目錄是不會變的(就算是執行子目錄的文件),還是需要完整的路徑的。from . 的意思,就是在當前運行文件的所在目錄下面找。

所以 我如果運行下面這個:

from test2 import sub4
sub4.test42.print_42()

跑不通的, test2/sub4/__init__.py沒有寫 import test42,所以sub4是不知道自己下面有test42這個模塊的

繼續 爲什麼我說是運行了那兩個__init__.py文件呢,我們稍微修改一下__init__.py文件:

# test2/__init__.py
from . import sub4
print("you have imported sub4")

# test2/sub4/__init__.py
from . import test41
print("you have imported test41")

再來運行這個代碼:

from test2 import sub4
sub4.test41.print_41()

# 輸出:
# you have imported test41
# you have imported sub4
# 41 (函數.print_41的輸出)

看到了吧 from test2 import sub4 照樣會運行 test2/__init__.py 這個文件

哎 反正涉及到 test2 就會運行 test2/__init__.py,就可以import sub4,import sub4 不就又運行了 test2/sub4/__init__.py 那我這樣寫不也可以嗎:

import test2
test2.sub4.test41.print_41()

當然可以了,完全跑的通,輸出也依然是:

# you have imported test41
# you have imported sub4
# 41 (函數.print_41的輸出)

但這樣可不行:

import test2
sub4.test41.print_41()

冷不丁的寫一個sub4,是不行的,在my_test.py裏sub4是沒有被定義的。

但是這樣寫好長啊,有沒有辦法短一點呢?有辦法的,把 test2/__init__.py 改成這個:

# test2/__init__.py
from .sub4.test41 import print_41 as p41
print("you have imported sub4")

然後 我就只需要寫,就可以了:

import test2
test2.p41()

# 輸出
# you have imported test41
# you have imported sub4
# 41 (函數.print_41的輸出)

其實一般情況下,init文件都是這樣寫的,不然寫了init 我們還要寫一大堆東西,那寫init幹嘛呢。

 

接下來 還要說一個變量,就是__all__:

看上邊這個東西

# test/__init__.py
from .sub1 import *
from . import sub2
from . import sub3
__all__=['sub1','sub3']
print("You have imported test")

# test/sub1/__init__.py
from . import test11,test12
__all__ = ['test11']
print("you have imported sub1")

# test/sub2/__init__.py
from . import test21
from . import test22
print('you have imported sub2')

# sub3 裏沒有init文件

然後我們分別運行下面的代碼 我直接把輸出都列出來了:

from test import *
print(dir())

# 輸出:
# you have imported sub1
# you have imported sub2
# You have imported test
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sub1', 'sub3']
# 看好了,輸出了 you have imported sub2 ,dir()沒有sub2,原因就是,sub2 不在__all__這個變量裏。那爲什麼會輸出那句話呢,原因就是 運行了test/__init__.py

import test
print(dir())
print(dir(test))
print(dir(test.sub1))
print(dir(test.sub2))
print(dir(test.sub3))
test.test11.print_11()

# 輸出:
# you have imported sub1
# you have imported sub2
# You have imported test
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test']
# ['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'sub1', 'sub2', 'sub3', 'test11']
# ['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'sub1', 'sub2', 'test11']
# ['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'test11', 'test12']
# ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
# 11

# 我們發現,在當前的py文件下,只有test這個包,但在test包中,不僅有 'sub1', 'sub2', 'sub3',還有一個 'test11'模塊,並且可以直接test.test11.print_11()調用裏面的函數
# 我們還發現,test.sub1包裏照樣有test11和test12兩個,test.sub2裏也照樣有test21和test22兩個,sub3下面啥也沒有
# 挨個解釋,只有當 from test import * 的時候,__all__纔有影響。當直接import test 時,__all__是影響不到test中子包的。dir(test)裏,是有sub2的
# 爲什麼test下會有 test11,因爲在init裏,寫的是 from .sub1 import * ,這就相當於把sub1包裏的模塊導入了test包裏,所以可以直接調用
# 爲什麼test下沒有 test12呢,因爲test12 不在 test/sub1/__init__.py 的__all__變量裏。
# 爲什麼test.sub1下會有test11和test12兩個,原因還是__all__是影響不到包裏面的模塊的。
# 爲什麼sub3下面啥也沒有,因爲sub3裏是沒有init的,他是不知道自己下邊有啥的

加深一下理解,把 test/__init__.py 改一下,改成這樣:

# test/__init__.py
from . import sub2
from . import sub3

__all__=['sub1','sub3']
print("You have imported test")

運行下面這個:

import test
print(dir())
print(dir(test))

# 輸出
# you have imported sub2
# You have imported test
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test']
# ['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'sub2', 'sub3']
# 完全沒有sub1什麼事了,原因就是在test裏沒有import sub1,test下是沒有那個子包的

from test import *
print(dir())
# 輸出
# you have imported sub2
# You have imported test
# you have imported sub1
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sub1', 'sub3']
# 哎!這下有了sub1了,但注意you have imported sub1這句話跑到最後了。原因是,__all__變量起了作用,並且是在運行完了init之後,才把sub1給弄進來的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章