__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文件的两种方式:

  1. python xxx.py
  2. 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,而只是把它当作普通文件夹来处理。
验证,分别在__init__.py和__main__.py文件中,打印sys.path,添加以下代码 :
import sys
print("sys.path====", sys.path)
测试1:python -m myproject
结果:
__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

  1. 为什么不像上面提到的那样,直接在 sys.path 前面加上一个空字符串来解决问题呢?
    因为,如果不是在当前路径下调用,空字符串就没效果了: python /path/to/hhlb
  2. 为什么不写 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)    收藏  举报

导航