3.2.__main__

予早 2024-10-05 22:44:53
Categories: Tags:

https://docs.python.org/3/library/__main__.html

Python中__main__用于标记顶层代码环境,有两种使用方式:

顶层代码环境

顶层代码环境是一个名为__main__的模块,xxx.py文件被import后会成为modulemodule__name__属性通常为.py的全限定名称xxx,有时会直接运行xxx.py文件,此时__name__会被置为__main__,表示本次运行该模块作为顶层代码环境。

# demo.py,python demo.py
import configparser
from concurrent.futures import process

print(configparser.__name__)  # configparser
print(process.__name__)  # concurrent.futures.process
print(__name__)  # __main__ 通常应为demo

五种形成顶层代码环境的情形

  1. 交互式解释器

    使用python命令进入Python解释器,当前所处即为顶层代码环境__main__模块

    >>> __name__
    '__main__'
    
  2. .py文件作为pyhton命令参数,即直接执行Python文件

    # demo.py,python demo.py
    print(__name__)  # __main__ 通常应为demo
    
  3. 将模块或包作为python命令的-m参数,即以模块方式执行Python文件

    # demo.py,python -m demo
    print(__name__)  # __main__ 通常应为demo
    
  4. python执行从标准输入来的程序

    $ echo "import this" | python
    # The Zen of Python, by Tim Peters
    #
    # Beautiful is better than ugly.
    #Explicit is better than implicit.
    ...
    
  5. python执行-c参数指定的命令

    $ python -c "print(1, 2, 3, __name__)"
    # 1 2 3 __main__
    

使用示例1

在一些情况下,一个.py文件即被其他文件导入又充当可执行脚本,由于将.py文件导入作为模块时会执行整个.py文件,而实际上部分代码应当只在作为脚本执行时才执行,此时通常使用if __name__ == '__main__'来判断是否是顶层代码环境,特别注意,if __name__ == '__main__'其下代码属于全局作用域,即使不执行其中定义的全局变量会影响其余代码,通常使用main函数(名称任意,习惯上使用main)作为入口函数以避免该影响。

错误示例

# echo.py

import shlex
import sys


def echo(phrase: str) -> None:
    """
    通常是核心业务代码
    """
    print(phrase)

phrase = shlex.join(sys.argv)
echo(phrase)  # 'E:\python\pyBaseCode\echo.py'

错误示例

# echo.py

import shlex
import sys


def echo(phrase: str) -> None:
    """
    通常是核心业务代码
    """
    print(phrase)

if __name__ == '__main__':
    phrase = shlex.join(sys.argv)
    echo(phrase)  # 'E:\python\pyBaseCode\echo.py'

正确示例

# echo.py

import shlex
import sys


def echo(phrase: str) -> None:
    """
    通常是核心业务代码
    """
    print(phrase)


def main() -> int:
    phrase = shlex.join(sys.argv)
    echo(phrase)
    return 0


if __name__ == '__main__':
    sys.exit(main())

# 'E:\python\pyBaseCode\echo.py'

__main__.py

__main__.py文件放在包下,以Python标准库venv为例:

venv
  ├── scripts
  ├── __init__.py
  └── __main__.py

当使用python -m形式执行模块时会执行__main__.py

# 创建虚拟环境
python -m venv c:\path\to\myenv

__mian__.py文件通常保持代码简洁不包含过多逻辑,一般仅提供入口函数,如venv/__main__.py简短且不使用if__name__=='__main__'asyncio/__main__.pyvenv/__main__.py复杂,其使用if __name__ == '__main__'但整体上也仅提供入口函数。

venv/__main__.py

https://github.com/python/cpython/blob/3.12/Lib/venv/__main__.py

import sys
from . import main

rc = 1
try:
    main()
    rc = 0
except Exception as e:
    print('Error: %s' % e, file=sys.stderr)
sys.exit(rc)

__main__.py中使用``依然生效,直接导入为其全限定名称

import asyncio.__main__
print(asyncio.__main__.__name__)  # 'asyncio.__main__'

import __main__

Python程序中顶层代码环境被具象为__main__模块,一次程序运行中有且只有一个__main__模块,其他模块可以通过import __main__将顶层代码环境导入。__main__模块机制:

# namely.py

import __main__

print(f"is 'my_name' in dir(__main__)? {'my_name' in dir(__main__)}")


def print_user_name():
    if not did_user_define_their_name():
        raise ValueError('Define the variable `my_name`!')

    if '__file__' in dir(__main__):
        print(__main__.my_name, "found in file", __main__.__file__)
    else:
        print(__main__.my_name)
# special.py
import __main__

print("in special")
# start.py

import sys

from namely import print_user_name

my_name = "yuzao"


def main():
    try:
        print_user_name()
    except ValueError as ve:
        return str(ve)


if __name__ == "__main__":
    sys.exit(main())

import special
    
# python start.py执行结果如下:
# is 'my_name' in dir(__main__)? False
# yuzao found in file E:\python\pyBaseCode\start.py