**
写在前面
**
最近打算写一个系列文章,名字就叫做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 World\n");
}
应该都很熟悉吧,这些叫做入口函数,编程语言都有一个入口函数,作为程序开始执行的入口,不然容易迷路。既然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__)
[ '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', '__name__']
你可以找一下,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__属性还有很多值得深究的细节,比如顶层包的概念,这个在相对导入里面会涉及到,并且也是一个很让人摸不着头脑的点,就留到下次来讲解吧。