让我们一个一个地看一下,来帮助你深入理解其中到底发生了什么。
首先打印的是“A”,显然,因为这是整个程序的第一行。
在Python程序中,代码是按顺序执行的——先执行第一行,然后第二行,直到最后一行。所以第一个打印出来的是“A”一点不奇怪。
但是有不少的人,甚至可以说来听我的课的很大一部分学员,都很意外接下来显示的竟然是“B”。他们基本上都很确信的认为“B”并不会在类的定义阶段显示,而是应该在(其它时机显示出来)……直到亲眼看到“B”被打印出来才相信这是真的。
导致这个问题的部分原因在于Python中的关键字“class”和“def”看起来非常相像。前者定义了一个新的类,后者定义了一个新的函数。都是以一个关键字开始,接着是一段代码。那他们理所当然的也应该以同样的方式运行,对吗?
可惜实际上“class”和“def”运行起来完全不同:“def”关键字创造了一个新函数,这没错。然而,这个函数的主体代码被不会立即执行,而是被编译成中间码,并返回一个函数对象。
这意味着只要函数主体没有包含任何语法错误(包含缩进错误),就能成功返回一个函数对象。比如下面这个函数,虽然在运行时会抛出一个错误,但在定义时却并无大碍:
def foo(): asdfafsafasfsafavasdvadsvas
然而在使用“class”这个关键字创建一个类的时候情况就不完全一样了。只要稍微想想就知道“class”应该和上面的运行方式不一样才合理。想想看:我定义的方法在我定义了类之后就已经生效了。这意味着这个类内部的“def”关键字一定已经执行了。并且,如果“def”已经在一个类内部执行,那么其他所有代码也都执行。
在大多数正常情况下,你不用从类的定义内部调用“print”。而是用“def”来定义类的方法或者给类属性赋值。但是如果要定义方法和赋值属性,“def”和赋值过程就需要立即执行。而不可能等到类的定义全部完成后再进行。
我还想说的是,在Python里,一个类的定义过程就像一个模块:在一个类的内部定义一个全局变量,和在这个类(模块)内部创建一个属性是一样的。当然了,类的内部代码是逐行运行的,这样就可以让我们可以使用一些装饰器,比如@staticmenthod和@property。
简而言之,使用“def” 在类的内部创建了一个新的函数对象,之后用和这个函数同样的名称创建了一个类级别的属性
。
所以,当你定义一个类时,Python在定义的同时顺次地执行每一行。这就是为什么接下来打印出来的是“B”。
为什么接下来打印出来的不是“C”呢?因为一个函数的主体在这个函数被定义的时候并没有执行。所以当我们定义init方法时,“print”没有立刻运行。然而,它在我们的程序结尾创建两个“Person”实例的时候各运行一次,这就是为什么“C”打印了两次。
然而,在“C”打印出来之前,类的定义仍然要先运行完毕。所以我们先看到了“D”(在类定义内部),然后才是“E”(紧接着类的定义完毕后)。
多年来,我发现理解一个类定义内部发生了什么有助于理解许多Python的基本原理,以及这种语言是如何实现的。同时,我发现甚至在我的高级Python课上,大量的开发人员没有做对这道题,这说明哪怕你使用Python很长时间了,你也不一定掌握了“class”和“def”之间的区别。
网友评论