【思考】Python声明和定义可以分开么?

简介: 撰写C/C++函数的时候,许许多多的人甚至是IDE都养成了声明和定义分开的习惯。这不但让代码更加简洁,也提升了可理解性。但是Python里面,存不存在声明和定义分开的情况呢?Python的声明定义是如何组织的呢?有没有方法能够近似实现呢?一次由浅入深的思考……

本博客共分为四个部分,每一个小部分都可以看作一个独立的小问题,但相互之间又联系紧密。


第一部分:

探究这个问题,还是因为编程的时候碰到了这个错误:

提示tcplink没有定义,tcplink是我自己写的一个给监听到的tcp连接请求分配新线程的函数,不过是写在了下面,就像这样:

如果是C++里面的话,解决这个问题很简单。在文件开头的时候,加上该函数的声明式就OK,这样不仅方便,还能最大限度的保持美观(雾)。但是问题来了,Python里面好像没有声明和定义这一说呀!

到底有没有呢?这个得要从Python脚本的运行机制来看了。

在C++里面,声明是告诉编译器我的程序里将会有这个符号,编译器将声明内容进行记录,在定义处记录入口,分配内存。换言之,由于C++是编译型语言,这就可以对完整的程序进行扫描,进行跨文本域的联系。

然而,Python却不可以,Python是解释型语言,虽然我们自己写的脚本是一个完整文件,但是在给Python解释器执行的时候,依然相当于是把脚本文件里的内容一行一行输入进解释器并执行。这就造成了如果执行的当前语句要调用tcplink,解释器立马会在之前输入的内容中寻找tcplink的定义并执行,如果无法执行则报错。因为有这个机制,直接就导致了不能像C/C++那样,先放个声明式在前面,在把定义放到其他地方。


第二部分:

有些人可能会问,拉倒吧,我编程的时候这样写,funcb在funcc前面,funcb内将执行funcc,为啥运行的时候什么错误都不会报呢?

这就更有的说了,我们先来把这一小段放到Python命令行交互模式下,看结果如何:

对,依然也是什么错误都没发生。那么,根据之前所说,Python解释器是进来一行解释一行,如果无法解释就会立马报错,为什么这里解释器读入了funcc,用户也没有进行funcc的定义,解释器却没有报错呢?

其实,“Python解释器是进来一行解释一行” 这种说法其实还不太严谨。细心的读者能够发现,当在Python解释器输入def funcb():并回车的时候,>>>变成了...,只有在funcb用户定义完成后并确认,才又会回到>>>。一般情况下,用户是输入一行回车,>>>不发生改变,并输出应该有的结果。所以这说明了什么呢?这说明了两点问题:

1.在定义funcb()的时候,用户的回车没有让输入的语句执行。

2.定义完成funcb()向解释器发送回车确认的时候,解释器也没有执行之前定义内的内容。(因为如果执行了,一定会报错,就像下面这样)

新的问题又出现了,所以之前解释器到底执行了什么?答案就是:执行了“定义funcb()”这一个语句。这样,第二部分开头的那个问题,我想大家心里应该有答案了。把第二部分开头的那段程序执行过程画个图来理解,就像这样:

简而言之,如果当前只是执行了“定义XXX”的语句,解释器并不关心你具体定义的内容,此时进来的内容,解释器暂不执行。所以由于之前的funcc()是funcb()内定义的内容,自然不执行,也就无关乎是否此时存在funcc()。但!如果我此时调用funcb,解释器就会转而执行funcb内的具体内容,此时如果funcc()还没有被定义,一定会报not defined的错误!咱们来验证一下:

看来,说的不错。而第二部分开头的代码,在funcc实际被执行的时候,已经获得了完整的定义,故就不会报not defined的错误拉~


第三部分:

那么,有没有什么方法,能够让我的Python程序看起来更加整洁美观——不让所谓的“主程序”文件内函数太多而显得杂乱呢?

其实,import是个挺不错的方法,稍微熟悉Python的人都明白,import可以引用其他地方的py等模块,还可以用from module import function的形式,单独引入指定模块内的指定函数、类。

甚至!!

对于一个从C++过来的人,真是傻了。Python的import相当于“把import的东西原封不动塞进import处一整坨”,所以由于是在函数定义内import的,所以import的东西只有在该函数内才可使用~然而C++/C的单独#include预处理指令则是做不到的。

等等。是不是错过了什么重要的东西……

是的,如果import直接导入本文件,是否是可以的呢?如果可以的话,那岂不是之前的例子中,在之前加上:

from 本文件 import funca/b/c,就可以实现类似C++函数声明的作用了?万一成功了,那岂不是……真香?

我们来试试:

看来,真香失败。那么,为什么这种方法不行呢?我们来回顾一下第二部分中部那个我自己画的流程图,由于Python解释器是进来一句执行一句,所以这里执行的内容是:导入daliywork中的funcb。执行这一句的时候,funcb没有定义,故导入失败。但必须说明的一点是,“import 文件本身” 是可行的!可见接下来这张演示事例:

3e720e7e47ad828782e5f035226a1e778993d1e8

(坏了,这里又埋下了一个种子,被导入的daliywork里面的导入的daliywork是否产生了执行?如果有,执行的内容显示到了哪里?)


第四部分:

最后一个问题,如果import整个文件自己本身,可不可以实现这种结果呢?(就和只执行定义XXX时解释器不会探究具体定义的内容一样,这种只执行导入整个文件的操作,解释器是否会关心整个文件内的具体内容呢?)

我们再来回顾一下第三部分内关于import一个很直观的解释(但不是很严谨):

好了,有了这个解释,我觉得大部分人心里已经有答案了。测试代码如下,我们直接执行来看一下结果:

异常栈首先提示funca没有定义,指向daliywork第六行,但这个错误又是因为daliywork的第三行import导致的。这是因为import整个文件后,相当于把除了import这句以外的部分,替换到了import本身的地方,然后执行这些代码,这时候,在引入的部分内,执行到funca,发现还是没有进行定义,这时候再报出funca没有进行定义的错误。需要注意,错误内报了funca没有经过定义,并不是报的执行文件中的funca没有定义(因为此时在源文件line3 import这一句就已经抛出异常了,程序已经停在这里了!),而是import的daliywork里面的第六行出现的错误。这一点十分重要,可以用以下的图解进行解释(PS:画图真好用):

虽然引入的内容里,包括了所有的定义内容,但是由于引入的文件也会发生执行,所以依然无法实现我们想要的效果……

看来,由于Python特殊的解释执行机制,导致了没什么方法可以只把函数的声明提前。以后写代码的时候,还是乖乖要么把定义的函数都放到其他文件通过import module方式导入,通过module.function()形式进行调用;要么乖乖放在要执行该函数的代码的前面吧……


最后献上一首小诗。


无题

声明定义要分离,Python解释行不行?

千变万化难模拟,绕了一圈空叹息。

目录
相关文章
|
4月前
|
Python
Python 面向对象编程:Python 中如何定义抽象类和接口?
Python 面向对象编程:Python 中如何定义抽象类和接口?
|
5月前
|
Python
|
6月前
|
Python
python之有关匿名函数和偏函数的定义,优点,以及使用方法
python之有关匿名函数和偏函数的定义,优点,以及使用方法
|
6月前
|
编译器 Python
python之局部变量和全局变量的定义,两者之间的区别和使用方法,global和nonlocal的定义和使用方法,可变与不可变类型的定义和示例
python之局部变量和全局变量的定义,两者之间的区别和使用方法,global和nonlocal的定义和使用方法,可变与不可变类型的定义和示例
|
4月前
|
Python
python面向对象编程如何定义一个类?
python面向对象编程如何定义一个类?
|
5月前
|
Python
python函数的定义
python函数的定义
|
3天前
|
索引 容器
06-python数据容器-list列表定义/list的10个常用操作/列表的遍历/使用列表取出偶数
06-python数据容器-list列表定义/list的10个常用操作/列表的遍历/使用列表取出偶数
|
3天前
05-python之函数-函数的定义/函数的参数/函数返回值/函数说明文档/函数的嵌套使用/函数变量的作用域
05-python之函数-函数的定义/函数的参数/函数返回值/函数说明文档/函数的嵌套使用/函数变量的作用域
|
25天前
|
Python
Python类定义:从小白到专家的旅程
Python类定义:从小白到专家的旅程
7 0
|
1月前
|
Python
Python中如何定义函数 以及实参和形参的区别
Python中如何定义函数 以及实参和形参的区别
8 1