attributeerror: __enter___python玄学系列(第一集):你真的看懂了if __name__ == "__main__"吗?

写在前面


最近打算写一个系列文章,名字就叫做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__可以显示包路径

我们建立这样一个目录结构:

4b96c87de249b32c31fc3b0a9ccb0751.png

在c.py文件中写下面代码:

print(__name__)

然后再d.py中这样写:

from a.b import c

运行d.py文件,结果为:

a.b.c

看到了吧,此时a.py文件的__name__属性变成了a.b.c,完完全全反映了它所在的包路径。

关于__name__属性还有很多值得深究的细节,比如顶层包的概念,这个在相对导入里面会涉及到,并且也是一个很让人摸不着头脑的点,就留到下次来讲解吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值