python 模塊中的 __init__.py __main__.py


1 概念

假設一個最簡單的Package如下:

 

(1) 如果你希望 python 將一個文件夾作為 Package 對待,那么這個文件夾中必須包含一個名為 __init__.py 的文件,即使它是空的。

(2) 如果你需要 python 將一個文件夾作為 Package 執行,那么這個文件夾中必須包含一個名為 __main__.py 的文件。


在實際中,可以將pkg作為一個文件夾執行:

python pkg

  

也可以將pkg作為一個Package執行:

python -m pkg

2. 那么,這兩者有什么區別呢?為此,我們做一個簡單的實驗。


(1) 案例1

在__init__.py寫入如下內容:

import sys

print('__init__')
print('__init__.__name__', __name__)
print('__init__.__package__', __package__)

在__main__.py寫入如下內容:

import sys

print('__main__')
print('__main__.__name__', __name__)
print('__main__.__package__', __package__)

print('sys.path', sys.path)

執行 python pkg 和 python -m pkg,對比一下它們的輸出結果:

E:\>python pkg
__main__
__main__.__name__  __main__
__main__.__package__
sys.path ['pkg', 'D:\\Software\\Python\\Python37\\python37.zip', 'D:\\Software\\Python\\Python37\\DLLs', 'D:\\Software\\Python\\Python37\\lib', 'D:\\Software\\Python\\Python37', 
'D:\\Software\\Python\\Python37\\lib\\site-packages']

  

E:\>python -m pkg
__init__
__init__.__name__ pkg
__init__.__package__ pkg
__main__
__main__.__name__  __main__
__main__.__package__ pkg
sys.path ['E:\\Python_Web\\day_learning', 'D:\\Software\\Python\\Python37\\python37.zip', 'D:\\Software\\Python\\Python37\\DLLs', 'D:\\Software\\Python\\Python37\\lib', 'D:\\Software\\Python\\Python37', 
'D:\\Software\\Python\\Python37\\lib\\site-packages']

  

可以看出:

(a) 當作文件夾執行的時候,__init__.py 不會被執行。在 __main__.py 來說,打印的變量 __package__ 是一個空字符串。
     當作模塊執行的時候,會先執行 __init__.py ,再執行 __main__.py 。對於 __main__.py 來說,變量 __package__ 是 Package 的名字(pkg)。
另外, __init__.py 和 __main__.py 中的 __name__變量的值也是不同的。


(b) 對於一個 Package 來說,既然 __init__.py 必須存在,並且當作為模塊執行的時候,它會先執行,我們就應該把入口函數 main() 定義在 __init__.py 中。
當我們使用模塊方式 -m 執行的時候, __init__.py 定義了 main() 函數,然后在 __main__.py 中調用它,就能實現我們統一入口的目的。

 

(2) 案例2


對 __init__.py 做如下修改:

  

import sys

print('__init__')
print('__init__.__name__', __name__)
print('__init__.__package__', __package__)

print('sys.path', sys.path)


def main():
    print('__init__.main()')

對 __main__.py 做如下修改:

import sys

print('__main__')
print('__main__.__name__', __name__)
print('__main__.__package__', __package__)

print('sys.path', sys.path)

import pkg
pkg.main()

執行 python pkg ,調用失敗;執行 python -m pkg,調用正常。對比一下它們的輸出結果:

E:\>python pkg
__main__
__main__.__name__ __main__
__main__.__package__
sys.path ['pkg', 'D:\\Software\\Python\\Python37\\python37.zip', 'D:\\Software\\Python\\Python37\\DLLs', 'D:\\Software\\Python\\Python37\\lib', 'D:\\Software\\Python\\Python37', 'D:\\Software\\Python\\Python37\\lib\\site-packages']
Traceback (most recent call last):
  File "D:\Software\Python\Python37\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "D:\Software\Python\Python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "pkg\__main__.py", line 15, in <module>
    import pkg
ModuleNotFoundError: No module named 'pkg'

  

E:>python -m pkg
__init__
__init__.__name__ pkg
__init__.__package__ pkg
sys.path ['E:\\Python_Web\\day_learning', 'D:\\Software\\Python\\Python37\\python37.zip', 'D:\\Software\\Python\\Python37\\DLLs', 'D:\\Software\\Python\\Python37\\lib', 'D:\\Software\\Python\\Python37', 
'D:\\Software\\Python\\Python37\\lib\\site-packages'] __main__ __main__.__name__ __main__ __main__.__package__ pkg sys.path ['E:\\Python_Web\\day_learning', 'D:\\Software\\Python\\Python37\\python37.zip', 'D:\\Software\\Python\\Python37\\DLLs', 'D:\\Software\\Python\\Python37\\lib',
'D:\\Software\\Python\\Python37', 'D:\\Software\\Python\\Python37\\lib\\site-packages'] __init__.main()

  

python pkg ,調用失敗原因在於sys.path 的第一個搜索路徑pkg,但作為文件夾執行時package確是一個是空字符串(找不到)。

對於 python pkg 的調用方式來說,由於 __init__.py 沒有被載入,python 解釋器並不知道自己正在一個 Package 下面工作。默認的,python 解釋器將 __main__.py 的當前路徑 pkg 加入 sys.path 中,然后在這個路徑下面尋找 pkg 這個模塊。顯然, pkg 文件夾下面並沒有 pkg 這個模塊,因此出錯。

對於 python -m pkg 的調用方式來說,由於 __init__.py 被事先載入,此時 python 解釋器已經知道了這是一個 package ,因此當前路徑(空字符串)被包含在 sys.path 中。然后再調用 __main__.py ,這時 import pkg 這個包就沒有問題了。

要理解這點,就要明白 __init__.py 是 python 解釋器將當前文件夾作為 Package 處理的必要條件。

如果沒有讀取到 __init__.py ,python 就不會認為當前的文件夾是一個 Package,而只是把它當作普通文件夾來處理。

既然找到了問題原因,那么只需要把當前路徑加入到 sys.path 中,就能解決這個問題。

修改后的 __main__.py 如下:

import sys

print('__main__')
print('__main__.__name__', __name__)
print('__main__.__package__', __package__)

if not __package__:
    import os
    path = os.path.join(os.path.dirname(__file__), os.pardir)
    sys.path.insert(0, path)
    del os
print('sys.path', sys.path)

import pkg
pkg.main()

  

 

 

執行 python pkg ,結果正常:

 

E:\>python pkg
__main__
__main__.__name__ __main__
__main__.__package__
sys.path ['pkg\\..', 'pkg', 'D:\\Software\\Python\\Python37\\python37.zip', 'D:\\Software\\Python\\Python37\\DLLs', 'D:\\Software\\Python\\Python37\\lib', 'D:\\Software\\Python\\Python37', 'D:\\Software\\Python\\Python37\\lib\\site-packages']
__init__
__init__.__name__ pkg
__init__.__package__ pkg
sys.path ['pkg\\..', 'pkg', 'D:\\Software\\Python\\Python37\\python37.zip', 'D:\\Software\\Python\\Python37\\DLLs', 'D:\\Software\\Python\\Python37\\lib', 'D:\\Software\\Python\\Python37', 'D:\\Software\\Python\\Python37\\lib\\site-packages']
__init__.main()

  

看到這里,有人可能會提出兩個問題:

1. 為什么不直接在 sys.path 前面加上一個空字符串來解決問題呢? 

答:因為,如果不是在當前路徑下調用,空字符串就沒效果了,比如執行 python /path/to/pkg。


2. 為什么不用 if __package__ == '' 來判斷 __package__ 的值呢?

答:這並不是偷懶。因為可能會出現這種調用(不推薦): python pkg/__main__.py 。而這種情況下, __package__ 的值就是 None 而不是 '' 了。

 零零散散的終於徹底理解了,高興

轉載:https://blog.csdn.net/ywcpig/article/details/51179547


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM