Python:__init__.py文件和、__all__、import、__name__、__doc__
有时候,很容易忽略一些看上去不起眼的小细节,可能人总是有那么点理所当然的想法。
就像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