本博客共分为四个部分,每一个小部分都可以看作一个独立的小问题,但相互之间又联系紧密。
第一部分:
探究这个问题,还是因为编程的时候碰到了这个错误:
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710102409743-1597876943.jpg?x-oss-process=image/resize,w_1400/format,webp)
提示tcplink没有定义,tcplink是我自己写的一个给监听到的tcp连接请求分配新线程的函数,不过是写在了下面,就像这样:
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710102643699-974358883.jpg?x-oss-process=image/resize,w_1400/format,webp)
如果是C++里面的话,解决这个问题很简单。在文件开头的时候,加上该函数的声明式就OK,这样不仅方便,还能最大限度的保持美观(雾)。但是问题来了,Python里面好像没有声明和定义这一说呀!
到底有没有呢?这个得要从Python脚本的运行机制来看了。
在C++里面,声明是告诉编译器我的程序里将会有这个符号,编译器将声明内容进行记录,在定义处记录入口,分配内存。换言之,由于C++是编译型语言,这就可以对完整的程序进行扫描,进行跨文本域的联系。
然而,Python却不可以,Python是解释型语言,虽然我们自己写的脚本是一个完整文件,但是在给Python解释器执行的时候,依然相当于是把脚本文件里的内容一行一行输入进解释器并执行。这就造成了如果执行的当前语句要调用tcplink,解释器立马会在之前输入的内容中寻找tcplink的定义并执行,如果无法执行则报错。因为有这个机制,直接就导致了不能像C/C++那样,先放个声明式在前面,在把定义放到其他地方。
第二部分:
有些人可能会问,拉倒吧,我编程的时候这样写,funcb在funcc前面,funcb内将执行funcc,为啥运行的时候什么错误都不会报呢?
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710110329601-513528875.jpg?x-oss-process=image/resize,w_1400/format,webp)
这就更有的说了,我们先来把这一小段放到Python命令行交互模式下,看结果如何:
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710110548688-343095556.jpg?x-oss-process=image/resize,w_1400/format,webp)
对,依然也是什么错误都没发生。那么,根据之前所说,Python解释器是进来一行解释一行,如果无法解释就会立马报错,为什么这里解释器读入了funcc,用户也没有进行funcc的定义,解释器却没有报错呢?
其实,“Python解释器是进来一行解释一行” 这种说法其实还不太严谨。细心的读者能够发现,当在Python解释器输入def funcb():并回车的时候,>>>变成了...,只有在funcb用户定义完成后并确认,才又会回到>>>。一般情况下,用户是输入一行回车,>>>不发生改变,并输出应该有的结果。所以这说明了什么呢?这说明了两点问题:
1.在定义funcb()的时候,用户的回车没有让输入的语句执行。
2.定义完成funcb()向解释器发送回车确认的时候,解释器也没有执行之前定义内的内容。(因为如果执行了,一定会报错,就像下面这样)
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710111719876-682766625.jpg?x-oss-process=image/resize,w_1400/format,webp)
新的问题又出现了,所以之前解释器到底执行了什么?答案就是:执行了“定义funcb()”这一个语句。这样,第二部分开头的那个问题,我想大家心里应该有答案了。把第二部分开头的那段程序执行过程画个图来理解,就像这样:
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710113441992-617539962.jpg?x-oss-process=image/resize,w_1400/format,webp)
简而言之,如果当前只是执行了“定义XXX”的语句,解释器并不关心你具体定义的内容,此时进来的内容,解释器暂不执行。所以由于之前的funcc()是funcb()内定义的内容,自然不执行,也就无关乎是否此时存在funcc()。但!如果我此时调用funcb,解释器就会转而执行funcb内的具体内容,此时如果funcc()还没有被定义,一定会报not defined的错误!咱们来验证一下:
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710114043896-505428296.jpg?x-oss-process=image/resize,w_1400/format,webp)
看来,说的不错。而第二部分开头的代码,在funcc实际被执行的时候,已经获得了完整的定义,故就不会报not defined的错误拉~
第三部分:
那么,有没有什么方法,能够让我的Python程序看起来更加整洁美观——不让所谓的“主程序”文件内函数太多而显得杂乱呢?
其实,import是个挺不错的方法,稍微熟悉Python的人都明白,import可以引用其他地方的py等模块,还可以用from module import function的形式,单独引入指定模块内的指定函数、类。
甚至!!
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710134337475-1666695031.jpg?x-oss-process=image/resize,w_1400/format,webp)
对于一个从C++过来的人,真是傻了。Python的import相当于“把import的东西原封不动塞进import处一整坨”,所以由于是在函数定义内import的,所以import的东西只有在该函数内才可使用~然而C++/C的单独#include预处理指令则是做不到的。
等等。是不是错过了什么重要的东西……
是的,如果import直接导入本文件,是否是可以的呢?如果可以的话,那岂不是之前的例子中,在之前加上:
from 本文件 import funca/b/c,就可以实现类似C++函数声明的作用了?万一成功了,那岂不是……真香?
我们来试试:
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710140332504-769401520.jpg?x-oss-process=image/resize,w_1400/format,webp)
看来,真香失败。那么,为什么这种方法不行呢?我们来回顾一下第二部分中部那个我自己画的流程图,由于Python解释器是进来一句执行一句,所以这里执行的内容是:导入daliywork中的funcb。执行这一句的时候,funcb没有定义,故导入失败。但必须说明的一点是,“import 文件本身” 是可行的!可见接下来这张演示事例:
![3e720e7e47ad828782e5f035226a1e778993d1e8](https://yqfile.alicdn.com/3e720e7e47ad828782e5f035226a1e778993d1e8.jpeg?x-oss-process=image/resize,w_1400/format,webp)
(坏了,这里又埋下了一个种子,被导入的daliywork里面的导入的daliywork是否产生了执行?如果有,执行的内容显示到了哪里?)
第四部分:
最后一个问题,如果import整个文件自己本身,可不可以实现这种结果呢?(就和只执行定义XXX时解释器不会探究具体定义的内容一样,这种只执行导入整个文件的操作,解释器是否会关心整个文件内的具体内容呢?)
我们再来回顾一下第三部分内关于import一个很直观的解释(但不是很严谨):
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710141059363-1132901238.jpg?x-oss-process=image/resize,w_1400/format,webp)
好了,有了这个解释,我觉得大部分人心里已经有答案了。测试代码如下,我们直接执行来看一下结果:
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710141555358-331023307.jpg?x-oss-process=image/resize,w_1400/format,webp)
异常栈首先提示funca没有定义,指向daliywork第六行,但这个错误又是因为daliywork的第三行import导致的。这是因为import整个文件后,相当于把除了import这句以外的部分,替换到了import本身的地方,然后执行这些代码,这时候,在引入的部分内,执行到funca,发现还是没有进行定义,这时候再报出funca没有进行定义的错误。需要注意,错误内报了funca没有经过定义,并不是报的执行文件中的funca没有定义(因为此时在源文件line3 import这一句就已经抛出异常了,程序已经停在这里了!),而是import的daliywork里面的第六行出现的错误。这一点十分重要,可以用以下的图解进行解释(PS:画图真好用):
![](https://images2018.cnblogs.com/blog/1266846/201807/1266846-20180710144557139-799675366.jpg?x-oss-process=image/resize,w_1400/format,webp)
虽然引入的内容里,包括了所有的定义内容,但是由于引入的文件也会发生执行,所以依然无法实现我们想要的效果……
看来,由于Python特殊的解释执行机制,导致了没什么方法可以只把函数的声明提前。以后写代码的时候,还是乖乖要么把定义的函数都放到其他文件通过import module方式导入,通过module.function()形式进行调用;要么乖乖放在要执行该函数的代码的前面吧……
最后献上一首小诗。
无题
声明定义要分离,Python解释行不行?
千变万化难模拟,绕了一圈空叹息。