__init__.py和__main__.py
一、__init__.py和__main__.py文件的区别
示例:
我们知道,在之前的python版本中,如果要想将一个文件夹作为包(package)来使用,必须要有__init__.py文件,即使它是空文件。当导入这个包时,__init__.py文件中的代码被执行。
如果直接运行一个文件夹(无论是普通文件夹,还是包),那么这个文件夹中必须包含一个名为 __main__.py
的文件,否则抛出异常(can't find '__main__' module in 'myproject')。当执行 python -m 文件夹名
或者 python 文件夹名
的时候,这个文件中的代码都会被执行。
__init__.py内容:
print('__init__ is called!!!') print('__init__.__name__ is: ', __name__) print('__init__.__package__ is: ', __package__)
__main__.py内容:
print('__main__ is called!!!') print('__main__.__name__ is : ', __name__) print('__main__.__package__ is : ', __package__)
测试之前需要明白,python加载py文件的两种方式:
- python xxx.py
- python -m xxx.py
第1种,叫做直接运行
第2种,相当于import,叫做当做脚本来启动
这是两种不同的加载py文件的方式,主要是影响sys.path这个属性
测试1:
python myproject # 直接运行
结果1:
__main__ is called!!! __main__.__name__ is : __main__ __main__.__package__ is :
说明1:
- 当我们将myproject当作文件夹直接执行的时侯,__init__.py不会被执行,__main__.py被执行。
- 其中,__main__.name的名字是__main__;__main__.__package__的包名为空,说明不是作为模块来运行的。
测试2:
python -m myproject # -m参数,是将模块用作脚本去运行
结果2:
__init__ is called!!! __init__.__name__ is: myproject __init__.__package__ is: myproject __main__ is called!!! __main__.__name__ is : __main__ __main__.__package__ is : myproject
说明2:
- 当模块作为脚本执行的时候,python 会先执行
__init__.py
,然后执行__main__.py
。 - 两者都有了__package__的值,即模块名称myproject
- __init__的
__name__为模块名称myproject,而__main__的__name__仍然是__main__。
- __main__的__name__无论何时调用,它的值都是__main__。
二、统一入口:在__ini__.py中定义一个main()函数,在__main__.py中调用它
对于一个 package 来说,既然 __init__.py
必须存在,而且当作为模块执行的时候,它会先执行,我们就应该把入口函数 main()
定义在 __init__.py
中。
修改__init__.py文件:
print('__main__ is called!!!') print('__main__.__name__ is : ', __name__) print('__main__.__package__ is : ', __package__) def main(): print('这是模块入口__init__.main(),todo something....')
说明: 执行python -m myproject,main()函数并不会被执行,因为这里并没有调用它,只是定义了一个入口函数:main()
如果我们在 __main__.py
中调用它,这就实现了我们统一入口的目的。
修改__main__.py文件:
print('__main__ is called!!!') print('__main__.__name__ is : ', __name__) print('__main__.__package__ is : ', __package__) import myproject myproject.main() print("__init__.py中定义的main函数被调用!!!")
测试1:以脚本的方式,执行python -m myproject
结果:
__init__ is called!!! __init__.__name__ is: myproject __init__.__package__ is: myproject __main__ is called!!! __main__.__name__ is : __main__ __main__.__package__ is : myproject 这是模块入口__init__.main(),todo something.... __init__.py中定义的main函数被调用!!!
这样就实现了统一入口!
三、直接运行时的异常
在上面,我们以脚本调用的方式,实现了统一入口。
如果此时,我们直接调用包名呢?
测试:以直接运行的方式,执行python myproject
结果:
__main__ is called!!! __main__.__name__ is : __main__ __main__.__package__ is : 然后抛出异常:ModuleNotFoundError: No module named 'myproject'
两种不同的调用方式,第1种直接运行的方式没有调用__init__.py文件,第2种以脚本的方式运行调用了__init.py文件。
对于 python -m hhlb
的调用方式来说,由于 __init__.py
被事先载入,此时 python 解释器已经知道了自己就是一个 package ,
因此当前路径被包含在 sys.path
中。然后再调用 __main__.py
,这时 import myproject
这个包就毫无压力了。
python hhlb
的调用方式来说,由于 __init__.py
没有被载入,python 解释器并不知道自己正在一个 package 下面工作。默认的,python 解释器将 __main__.py
的当前路径 myproject
加入 sys.path
中,然后在这个路径下面寻找 myproject
这个模块。显然, myproject
文件夹下面并没有 myproject
这个模块,因此出错。__init__.py
是 python 解释器将当前文件夹作为 package 处理的必要条件。如果没有读取到 __init__.py
,python 就不会认为当前的文件夹是一个 package,而只是把它当作普通文件夹来处理。import sys print("sys.path====", sys.path)
__init__ is called!!! __init__.__name__ is: myproject __init__.__package__ is: myproject sys.path==== ['C:\\Users\\Administrator\\PycharmProjects\\untitled2', 'C:\\Users\\Administrator\\AppData\ \Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\P ython\\Python37\\DLLs', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\ \Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\Administrator\\AppData\\L ocal\\Programs\\Python\\Python37\\lib\\site-packages', 'C:\\Users\\Administrator\\AppData\\Local\\Program s\\Python\\Python37\\lib\\site-packages\\scapy-2.4.2-py3.7.egg'] __main__ is called!!! __main__.__name__ is : __main__ __main__.__package__ is : myproject sys.path==== ['C:\\Users\\Administrator\\PycharmProjects\\untitled2', 'C:\\Users\\Administrator\\AppData\ \Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\P ython\\Python37\\DLLs', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\ \Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\Administrator\\AppData\\L ocal\\Programs\\Python\\Python37\\lib\\site-packages', 'C:\\Users\\Administrator\\AppData\\Local\\Program s\\Python\\Python37\\lib\\site-packages\\scapy-2.4.2-py3.7.egg'] 这是模块入口__init__.main(),todo something.... __init__.py中定义的main函数被调用!!!
测试2:python myproject
结果:包名为空,说明当前myproject不是作为一个包来运行。
__main__ is called!!! __main__.__name__ is : __main__ __main__.__package__ is : sys.path==== ['myproject', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\python3 7.zip', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\Adminis trator\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\Administrator\\AppData\\Local\\Prog rams\\Python\\Python37', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site -packages', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages\\sc apy-2.4.2-py3.7.egg'] 抛出异常,import myproject失败
四、解决问题
当直接运行包的时侯(python myproject),myproject不是作为一包来运行,因此包的路径myproject没有被加入sys.path路径中。
解决办法,将myproject包所在路径,加入到sys.path即可
修改__main__.py文件:
print('__main__ is called!!!') print('__main__.__name__ is : ', __name__) print('__main__.__package__ is : ', __package__) import os import sys if not __package__: path = os.path.join(os.path.dirname(__file__), os.pardir) sys.path.insert(0, path) print("sys.path====", sys.path) import myproject myproject.main() print("__init__.py中定义的main函数被调用!!!")
测试:python myproject
结果:
__main__ is called!!! __main__.__name__ is : __main__ __main__.__package__ is : sys.path==== ['myproject\\..', 'myproject', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\ Python37\\python37.zip', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C :\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\Administrator\\App Data\\Local\\Programs\\Python\\Python37', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Py thon37\\lib\\site-packages', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib\\ site-packages\\scapy-2.4.2-py3.7.egg'] __init__ is called!!! __init__.__name__ is: myproject __init__.__package__ is: myproject sys.path==== ['myproject\\..', 'myproject', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\ Python37\\python37.zip', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C :\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\Administrator\\App Data\\Local\\Programs\\Python\\Python37', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Py thon37\\lib\\site-packages', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib\\ site-packages\\scapy-2.4.2-py3.7.egg'] 这是模块入口__init__.main(),todo something.... __init__.py中定义的main函数被调用!!!
说明:直接运行的方式,本身应该只会执行__main__.py,不会执行__init__.py文件。
但是由于我们import 包,此时将执行__init__.py中的代码;最后调用了myproject.main()函数,此时执行了__init__.py文件中的main()函数。
QA
- 为什么不像上面提到的那样,直接在
sys.path
前面加上一个空字符串来解决问题呢?
因为,如果不是在当前路径下调用,空字符串就没效果了:python /path/to/hhlb
。 - 为什么不写
if __package__ == ''
来判断__package__
的值呢?这样应该更准确。
这并不是偷懒。因为你可能会变态到使用这种方法调用:python hhlb/__main__.py
。而这种情况下,__package__
的值就是None
而不是''
了。
参考:https://www.jianshu.com/p/cb97d290c17f
posted on 2019-08-17 21:25 myworldworld 阅读(1602) 评论(0) 收藏 举报