写在前面
最近打算写一个系列文章,名字就叫做python玄学系列吧。在我学习的过程中,发现了很多python乍看之下非常令人费解的用法,或者是看似简单,实际上有着更加深层次的知识点。比如今天我将要介绍的这句话。几乎所有刚学python的同学都会遇到,但真正能明白它的深层次用法的却很少。废话不多说,下面就让我们开始吧!!!
1、本质就是个if判断
仔细看这句代码,就是一个if判断,但它又不是一个简单的if判断。下面来一步一步揭开它的真面目。
if __name__ == "__main__",举个例子,我的名字叫小北,所以在别人眼中,你的名字叫小北,__name__=="小北";在自己眼中,你是你自己,__name=="__main__",main是我自己的另一个别称,任何人在自己的眼中都可以叫做main,意思就是说:“我就是我,是颜色不一样的烟火”。
专业一点的说法就是:当.py文件被直接运行时,if __name__ == '__main__'之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ == '__main__'之下的代码块不被运行。
好了,全文结束。
2、入口函数
其实在第一节中,真的已经说完了if __name__ == "__main__"的全部含义,不过我知道,你依然还是有很多疑惑,这里的__name__是个什么东西?为什么很多博客说是入口函数,这和入口函数有什么关系?接下来我给大家一一解释这些问题。
学过c++/c/java的同学,应该知道main方法吧,java里面长这样的:
public static void main(String[] args) {
System.out.println("Hello Word");
}
c语言里面是这样的:
#include <stdio.h>
void main(){
printf("Hello Worldn");
}
应该都很熟悉吧,这些叫做入口函数,编程语言都有一个入口函数,作为程序开始执行的入口,不然容易迷路。既然java/c都有入口函数,那么python有没有呢?准确来说是没有的。但是人为的造了一个入口出来(这就叫做世上本没有路,走的人多了便成了路)。别急,我慢慢给你解释。首先说一下python与java/c的区别,python属于解释型语言,java/c叫做编译型语言。他们最大的不同就在于,python是从上到下,一行一行执行,解释一行,执行一行。c是编译型语言,先编译后执行。
编译型语言在程序执行之前,有一个单独的编译过程,将程序翻译成机器语言,以后执行这个程序的时候,就不用再进行翻译了。
解释型语言,是在运行的时候将程序翻译成机器语言,所以运行速度相对于编译型语言要慢。
其实java不能算严格的编译型语言
虽然Java程序在运行之前也有一个编译过程,但是并不是将程序编译成机器语言,而是将它编译成字节码(可以理解为一个中间语言)。
在运行的时候,由JVM将字节码再翻译成机器语言。所以java是先编译后解释。
所以python的一个.py文件是没有入口的,因为都是从上到下执行,要什么自行车啊(入口)。那么问题就来了,一个.py文件是可以被另外的模块import的,既然没有入口,那么被import的模块的所有代码都会执行,这并不是我们想要的,有时候想要控制一部分代码不执行那该怎么办呢。这个时候 if __name__ == "__main__"就出场了。
来看一下下面的代码:
我有一个a.py文件:
aa = 1
def aaa():
print("aaa")
aaa()
现在有一个b.py文件导入了a.py:
import a
def bb():
print("bbb")
bb()
结果为:
aaa
bbb
问题就来了,我期望的只是打印“bbb”, 结果把“aaa”也给打印出来了,显然不是我想要的,怎么办呢,a.py代码改成这样就行了:aa = 1
print(aa)
def aaa():
print("aaa")
if __name == "__main__":
aaa()
运行python a.py命令结果为:
aaa
运行python b.py命令为:
1
bbb
看吧,这就是if __name == "__main__":作为入口函数的用法。这里还有一个细节,aa变量在if __name == "__main__":之前,并且它的值也被打印出来了,这就证明了一点,if __name == "__main__":只是一个人为(假的)的入口函数,只能阻止它所包含的代码运行。
可能你还是会问,为什么运行a.py就能执行aaa()函数,运行b.py就不会执行呢。这就该__name__上场了。
3、令人费解的__name__
“__name__”是一个python内建的系统变量。这一点可以通过dir(__builtins__)来查看
在终端执行dir(__builtins__):
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']>>> dir(__builtins__)['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
你可以找一下,list里面有一个__name__变量。具体它有什么用处呢,我们来实验一下。
修改刚才的a.py文件:
print(__name__)
运行a.py文件,结果为:
__main__
看到了吧,每个.py文件都有一个__name__属性,就像每条狗都有一个名字一样,有的叫哈士奇,有的叫柴犬,有的叫泰日天。反正都有一个名字。但是有别人有可能觉得你名字不好听,本来叫哈士奇,偏偏叫你二哈,你还不是得屁颠屁颠的嗷嗷叫。这些.py文件也是一样,当其他模块导入a.py文件的时候,它的__name__属性值就变了。
我们修改b.py文件:
import a
运行b.py文件,结果为:
a
这个时候a.py文件的__name__属性就不是__main__了,而是a,因为"a" == "__main__"是不成立的,自然就不会运行if __name == "__main__":下面的代码了。
4、__name__可以显示包路径
我们建立这样一个目录结构:
在c.py文件中写下面代码:
print(__name__)
然后再d.py中这样写:
from a.b import c
运行d.py文件,结果为:
a.b.c
看到了吧,此时a.py文件的__name__属性变成了a.b.c,完完全全反映了它所在的包路径。
关于__name__属性还有很多值得深究的细节,比如顶层包的概念,这个在相对导入里面会涉及到,并且也是一个很让人摸不着头脑的点,就留到下次来讲解吧。