有時候,很容易忽略一些看上去不起眼的小細節,可能人總是有那么點理所當然的想法。
就像init.py文件,是不是感覺像個熟悉的陌生人?~~~
這里來淺析一下一些基礎概念(都是左右各雙下划線)。
1. __init__.py 文件:這個文件,就像一個標識符一樣,用來表明一個文件夾是python包還是一般文件夾,如果文件夾中存在該文件,就是python包;可以試試pycharm直接建立package,就會發現剛建完就已經存在__init__.py文件了。
這個文件用處:
用處一:當用於import對象時,可以識別出哪些是可以導入的包,哪些只是一般文件夾;
用處二:可以在其中導入需要的對象,然后通過在執行程序中import package來引用這些包,所以簡化了執行程序,因為每次導入package的時候會自動執行__init__.py;但最好不要在這里寫自己的模塊,該文件越簡單越好;
用處三:__all__參數,只用於指定 from package import * 時,導入的包是哪些,不需要的包可以先不導入;並不影響from package import module/package、import package.module等形式的導入。
注意:該文件可以為空,即不做操作,但是package必須要有這個文件。
2. import:用於導入包、函數、變量、類等;那么import干了些啥?(sys.path是可以修改的,且從package包導入模塊需要用 from package import ... 形式)
3. __name__:用於判斷當前模塊是不是主程序文件(主執行),也就是查看 __name__ 的值是否為 '__main__' ,如果是則該程序屬於主程序文件,如果不是則顯示該文件的文件名;
4. __doc__:模塊的注釋文本,例如函數或者類的說明,用 '''...''' 三引號形式包圍;
5. 示例說明:
(1)首先文件夾和文件如下:可見my_init是一個python包,package_test也是一個包,my_init_2是一個普通文件夾。
(2)首先兩個 __init__.py都為空,此時import模塊的操作如下:
## import_test.py 內容 class BBB(): def __init__(self, kk): self.kk = kk print(self.kk) def gogogo(self): print('---test class with import---') def ss_B(): print('---------------- this is the imported *.py file --------------') def tt_B(): print('this is also from imported file') print('it is very bad') print(__name__) if __name__ == "__main__": print(__name__) ss_B()
- main_test.py導入import_test模塊 —— 兩個py文件在同一目錄下
# 直接導入,main_test.py import import_test as it it.ss_B() print(__name__)
很明顯,前兩個結果來自導入import_test時,自動執行了該模塊 import_test中的內容(輸出的結果1和結果2);而為啥沒繼續執行下面的 if __name__='__main__' 里面的內容,那是因為此時__name__的值為:import_test(輸出的結果2),並不是__main__,所以不會執行。
然后,結果3調用了import_test模塊內的函數,結果4返回__main__,表明當前模塊(main_test.py)屬於主程序。
- main_test.py導入import_test模塊,同一目錄,也可以如下:
# main_test.py
from import_test import ss_B #可以直接通過py文件導入函數,模塊可以通過 from module_name import func 或者直接 import module_name;但是package要通過from package_name import module_name 或者 import package_name.module_name def ss(): print('---------------- this is the main *.py file --------------') if __name__ == "__main__": print(__name__) #用於判斷主程序是當前程序還是其他程序 ss() ss_B()
此時不能用:from my_init import import_test,為什么呢?因為my_init這個文件不在搜索路徑下,但是它的子目錄在搜索路徑(D:\\Python_workspace\\Felix_test\\test_init\\my_init)下,可以使用sys.path查看:
如果要是用,需要先將該目錄加入搜索路徑(它本身的目錄為 —— D:\Python_workspace\Felix_test\test_init\my_init),通過絕對地址加入:
# main_test.py
import sys,os sys.path.append(os.path.abspath(r'D:\Python_workspace\Felix_test\test_init')) #只有test_init目錄下才包含my_init目錄 from my_init import import_test
- main_test.py導入package_test包中的模塊,main_test和package_test包同一目錄:
# sub_test.py 內容 def sub_packege_test(): '''testing on the sub_packege_test doc ---''' print('it is a sub-package!')
則,調用時,__doc__表示說明文本(每個對象,例如函數、類、模塊都有這個屬性):
# main_test.py from package_test import sub_test #導入包中的文件 sub_test.sub_packege_test() print(sub_test.sub_packege_test.__doc__) #該函數說明
也可:
from package_test.sub_test import sub_packege_test #導入包內*.py文件中的函數 sub_packege_test()
- my_init_2文件夾中的test_1.py可以直接調用test_2.py,同一目錄,雖然這個文件夾不是package
# test_2.py 內容 def xx(): print('-----what happended here-----') print(xx())
# --------------------------------------------------------------------------- # test_1.py 內容 import test_2 as t2 t2.xx() print('it is test_1.py')
- my_init_2文件夾中的test_2,無法直接import my_init包中的import_test.py模塊,需要先添加搜索路徑;反之,也可以在my_init包中的模塊,import另一個文件夾(my_init_2)中的模塊 —— 注意:這可能會失敗,因為你import的模塊有可能會導入import其他的包,但是這些其他包又不在搜索路徑中時,就會報錯。
# test_2.py 內容 import os import sys sys.path.append(os.path.abspath(r'D:\Python_workspace\Felix_test\test_init')) #絕對路徑導入包,先將文件夾加入搜索路徑,然后導入;相對路徑個人感覺不太好用 from my_init import import_test import_test.ss_B() def xx(): print('-----what happended here-----') print(xx())
所以,最好不要嵌套太多層來進行import。。。
(3)使用__init__.py方法
- main_test.py中import package_test中的模塊
首先,修改my_init包的package_test中的__init__.py文件如下(使用__all__變量):
__all__ = ['sub_test'] #可以只選部分,不用導入過多包,只會影響 from package_test import * 中的導入結果 print('this is from __init__ file, sub-package')
然后,import使用:
# sub_test.py 內容 def sub_packege_test(): '''testing on the sub_packege_test doc ---''' print('it is a sub-package!') #------------------------------------------------- # main_test.py from package_test import * sub_test.sub_packege_test() print(__name__) sub_test_2.sub_packege_test_2()
可見:當使用 from package_test import * 時,讀取到的包均屬於package_test的 __init__.py中 __all__變量的值,不在該變量值中的包不會讀取。
- 此時,如果__init__.py中沒有某個模塊,而你又要使用時,可以在執行文件中自己導入:
# main_test.py
from package_test import sub_test_2 # 這種形式的導入與__init.py中的__all__參數無關 sub_test_2.sub_packege_test_2()
- 如果,不使用__all__變量,直接在__init__.py中import需要的包:
# package_test中的__init__.py文件內容: from package_test import sub_test print('this is from __init__ file, sub-package')
這種情況與直接在需要的地方通過from package import module,來導入模塊差不多;只不過如果使用了__init__.py文件,那么就會執行一次__init__.py文件,將所有需要的包一次性導入,在執行文件中就不需要寫很多import語句,而只需要導入包package即可,然后通過包來引用模塊;同時此時如果該文件里面還有其他代碼也會執行。
# main_test.py from package_test import sub_test #這種形式,如果有很多包則會比較麻煩 #import sub_test #直接導入該模塊不行 sub_test.sub_packege_test() # 所以可以這樣,通過package包來引用,如果在__init__.py中寫了很多個from package import module,則都可以這樣使用;也就是簡化了很多import語句
# main_test.py
import package_test package_test.sub_test.sub_packege_test()
注意:
1. from module/package import func/module:只是將某模塊或者package的一部分導入當前命名空間;
2. import語句中,如果導入的是模塊則會執行該模塊代碼;如果是package,則會執行package包的__init__.py文件;
3. sys.modules 和 sys.path的官方鏈接、解釋:
4. __init__.py文件越簡單越好,也可以為空。
5. __init__.py的import操作,主要是可以簡化執行程序中的import語句;而__all__只用於控制from package import * 中導入的包。
6. 以上都是用的絕對導入import;其中相對導入和絕對導入。
#
參考:
https://blog.csdn.net/fitzzhang/article/details/78988155
https://www.jianshu.com/p/dacbed54d063
https://www.runoob.com/python/python-modules.html
https://blog.csdn.net/weixin_38256474/article/details/81228492