【小思考】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整个文件自己本身,可不可以实现这种结果呢?(就和只执行定义XXX时解释器不会探究具体定义的内容一样,这种只执行导入整个文件的操作,解释器是否会关心整个文件内的具体内容呢?)
我们再来回顾一下第三部分内关于import一个很直观的解释:

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

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

虽然引入的内容里,包括了所有的定义内容,但是由于引入的文件也会发生执行,所以依然无法实现我们想要的效果……
看来,由于Python特殊的解释执行机制,导致了没什么方法可以只把函数的声明提前。以后写代码的时候,还是乖乖要么把定义的函数都放到其他文件通过import module方式导入,通过module.function()形式进行调用;要么乖乖放在要执行该函数的代码的前面吧……
无题
声明定义要分离,Python解释行不行?
千变万化难模拟,绕了一圈空叹息。
【小思考】Python里面有声明和定义分离这一说么?的更多相关文章
- C++模板编程:如何使非通用的模板函数实现声明和定义分离
我们在编写C++类库时,为了隐藏实现,往往只能忍痛舍弃模版的强大特性.但如果我们只需要有限的几个类型的模版实现,并且不允许用户传入其他类型时,我们就可以将实例化的代码放在cpp文件中实现了.然而,当我 ...
- 为什么C++中声明和定义要分开写
现在开始写项目了,你会发现我们一般都要写一个cpp,对应的还得有一个h文件,那么为什么在C++中我们要这么做? .h就是声明,.cpp就是实现,而所谓分离式实现就是指"声明"和&q ...
- 变量声明和定义的关系------c++ primer
为了允许把程序分成多个逻辑部分来编写,c++语言支持分离式编译机制 为了支持分离式编译,c++语言把声明和定义区分开来.声明(declaration)使得名字为程序所知,一个文件如果想使用别处定义的名 ...
- [C++]变量声明与定义的规则
声明与定义分离 Tips:变量能且仅能被定义一次,但是可以被多次声明. 为了支持分离式编译,C++将定义和声明区分开.其中声明规定了变量的类型和名字,定义除此功能外还会申请存储空间并可能为变量赋一个初 ...
- 使用 c++ 模板显示实例化解决模板函数声明与实现分离的问题
问题背景 开始正文之前,做一些背景铺垫,方便读者了解我的工程需求.我的项目是一个客户端消息分发中心,在连接上消息后台后,后台会不定时的给我推送一些消息,我再将它们转发给本机的其它桌面产品去做显示.后台 ...
- C/C++中的声明与定义
含义 声明(Declaration), 用于告诉编译器被声明的函数/变量的存在, 及它们的类型/调用格式信息, 以检查是否被正确调用. 声明不分配内存空间. 定义(Definition), 用于告诉编 ...
- 【转】变量的声明和定义,从C到编译原理到C++,再到Java
基础学了太久,时间一长有些东西就可能记得不太清楚,俗话说得好,"好记性不如烂笔头",所以把基础中的基础-变量的声明和定义,从C到编译原理到C++,再到Java用烂笔头记录下来 最早 ...
- OC基础--OC中类的声明与定义
OC中设计一个类的步骤: 一.声明类: 1.用到的关键字--@interface 和 @end 2.类名 3.继承NSObject 4.属性 5.方法(行为,只需要声明) 二.实现(定义)类 1.用到 ...
- 你好,C++(24)好大一个箱子!5.1.1 函数的声明和定义
第5章 用函数封装程序功能 在完成功能强大的工资程序V1.0之后,我们信心倍增,开始向C++世界的更深远处探索. 现在,我们可以用各种数据类型定义变量来表达问题中所涉及的各种数据:用操作符连接这些变量 ...
随机推荐
- form:select form:options 标签数据回显
在jsp页面中经常会使用到 form:select form:options 遍历后台List集合生成 select 下拉选择器,但是 form:options 标签并没有提供一个可以回显数据的属性. ...
- Uva5211/POJ1873 The Fortified Forest 凸包
LINK 题意:给出点集,每个点有个价值v和长度l,问把其中几个点取掉,用这几个点的长度能把剩下的点围住,要求剩下的点价值和最大,拿掉的点最少且剩余长度最长. 思路:1999WF中的水题.考虑到其点的 ...
- Web 开发人员需知的 Web 缓存知识
今天踩着前辈们的肩膀,再次把这篇文章翻译整理下.一来让自己对web缓存的理解更深刻些,二来让大家注意力稍稍转移下,不要整天HTML5, 面试题啊叨啊叨的~~ 什么是Web缓存,为什么要使用它? Web ...
- 【CodeForces】708 B. Recover the String 数学构造
[题目]B. Recover the String [题意]找到一个串s,满足其中子序列{0,0}{0,1}{1,0}{1,1}的数量分别满足给定的数a1~a4,或判断不存在.数字<=10^9, ...
- 微信小程序rpx单位
rpx单位是微信小程序中css的尺寸单位,rpx可以根据屏幕宽度进行自适应.规定屏幕宽为750rpx.如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375p ...
- lintcode 40. 用栈实现队列
使用两个栈来回倒腾可以实现队列. AC代码: import java.util.Stack; public class Queue { private Stack<Integer> sta ...
- 服务器端包含 SSI简介
服务器端包含 SSI,是英文 Server Side Includes的简写.SSI是一种可以指挥服务器动态声称网页内容的HTML指令. 通常SSI可以用来确保网页中的一些通用内容,比如版权信息.联系 ...
- spring3-spring的事务管理机制
1. Spring的事务管理机制 Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的: PlatformTransactionManager:事务管理器—主要用于平 ...
- WordPress浏览数插件的安装使用
插件安装很容易,但是和大多插件都一样,安装后需要调用代码才能显示,我安装后,也调用了.但是就是不显示,后来才发现,我从其他地方复制过来的代码,函数是中文的单引号,这样致使函数失效,注意代码中参数的引号 ...
- C语言回调函数总结
/* Main program ---calls--> Library function ---calls--> Callback funtion */ #include <stdi ...