Skip to content

可执行的 Python 文件

24 / 44

理论上来讲你最终可以把任何一个程序无论大小都封装或者囊括到仅仅一个函数之中按照惯例(Convention),这个函数的名称叫做 main()

python
def routine_1():
    print('Routine 1 done.')

def routine_2():
    sub_routine_1()
    sub_routine_2()
    print('Routine 2 done.')

def sub_routine_1():
    print('Sub-routine 1 done.')

def sub_routine_2():
    print('Sub-routine 2 done.')

def main():
    routine_1()
    routine_2()
    print('This is the end of the program.')

if __name__ == '__main__':
    main()
plaintext
Routine 1 done.
Sub-routine 1 done.
Sub-routine 2 done.
Routine 2 done.
This is the end of the program.

当一个模块其实就是存有 Python 代码的 .py 文件例如mycode.py import 语句导入的时候这个模块的 __name__ 就是模块名例如'mycode')。

而当一个模块被命令行运行的时候这个模块的 __name__ 就被 Python 解释器设定为 '__main__'

把一个程序整个封装到 main() 之中而后在模块代码里加上

python
if __name__ == '__main__':
    main()

这么做的结果是

  1. Python 文件被当作模块 import 语句导入时if 判断失败main() 函数不被执行
  2. Python 文件被 python -m 运行的时候if 判断成功main() 函数才被执行

还记得那个 Python 的彩蛋吧this.py 的代码如下

python
s = """Gur Mra bs Clguba, ol Gvz Crgref
Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))

所以只要 import thisthis.py 中的代码就被执行

python
import this

我在当前目录下保存了一个文件 that.py它的内容如下 —— 其实就是把 this.py 之中的代码封装到 main() 函数中了

python
# %load that.py
def main():

    s = """Gur Mra bs Clguba, ol Gvz Crgref
    Ornhgvshy vf orggre guna htyl.
    Rkcyvpvg vf orggre guna vzcyvpvg.
    Fvzcyr vf orggre guna pbzcyrk.
    Pbzcyrk vf orggre guna pbzcyvpngrq.
    Syng vf orggre guna arfgrq.
    Fcnefr vf orggre guna qrafr.
    Ernqnovyvgl pbhagf.
    Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
    Nygubhtu cenpgvpnyvgl orngf chevgl.
    Reebef fubhyq arire cnff fvyragyl.
    Hayrff rkcyvpvgyl fvyraprq.
    Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
    Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
    Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
    Abj vf orggre guna arire.
    Nygubhtu arire vf bsgra orggre guna *evtug* abj.
    Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
    Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
    Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

    d = {}
    for c in (65, 97):
        for i in range(26):
            d[chr(i+c)] = chr((i+13) % 26 + c)

    print("".join([d.get(c, c) for c in s]))

if __name__ == '__main__':
    main()

于是当你在其它地方导入它的时候import thatmain() 函数的内容不会被执行

python
import that

但是你在命令行中 python that.py或者 python -m that that.py 当作可执行模块运行的时候main() 就会被执行 —— 注意不要写错python -m that.py 会报错的 —— -m 参数就不要写文件尾缀 .py

bash
%%bash
python that.py
bash
%%bash
python -m that

that.py 那样把整个程序放进 main() 函数之后import that 不会自动执行 main 函数里的代码不过你可以调用 that.main():

python
import that
that.main()

当然that.py 之中没有任何 Docstring,所以 help(that) 的结果是这样的

python
import that
help(that)

所以之前那个从 37 万多个词汇中挑出 3700 多个字母加起来等于 100 的词汇的程序也可以写成以下形式

python
#!/usr/bin/env python

def sum_of_word(word):
    sum = 0
    for char in word:
        sum += ord(char) - 96
    return sum
def main(wordlist, result):
    with open(result, 'w') as result:
        with open(wordlist, 'r') as file:
            for word in file.readlines():
                if sum_of_word(word.strip()) == 100:
                    result.write(word)

if __name__ == '__main__':
    main('words_alpha.txt', 'results.txt')

至于以上代码中的第一行#!/usr/bin/env python 是怎么回事建议你自己动手解决一下 Google:

python3 script executable

你会很快弄明白的……

另外再搜索一下

python3 script executable parameters retrieving

你就可以把以上程序改成在命令行下能够接收指定参数的 Python 可执行文件……

顺带说import this 的彩蛋有更好玩的玩法

python
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

import this
love = this
this is love                            # True
love is True                            # False
love is False                           # False
love is not True or False               # True
love is not True or False; love is love # True True
plaintext
True
False
False
True
True
True

Terminal 里输入 python ⏎ 而后在 Interactive Shell 里逐句输入试试love = this 后面的每一句都是布尔运算想想看为什么是那样的结果

python
import this
love = this

this is love
# True, 试试看,id(this) 和 id(love) 是同一个值
# 即,它们的内存地址相同

love is True
# False, id(love) 和 id(True) 不是同一个值
love is False
# 同上

love is not True or False
# is not 的优先级比 or 高;所以相当于是:
# (love is not True) or False,于是返回 True

love is not True or False; love is love
# 重复一次上一句 —— `;` 是语句分隔符
# 而后 love is love 当然是 True

注意以下代码中id() 函数的输出结果

python
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

import this
love = this
this is love
love is True
love is False
love is not True or False
love is not True or False; love is love
id(love)
id(this)
id(True)
id(False)
love is not True
plaintext
True
False
False
True
True
True
4345330968
4345330968
4308348176
4308349120
True

Python 的操作符优先级完整表格在这里

Operator precedence

Python 的更多彩蛋

Python Easter Eggs