Python编程代码Bug,求解!

昨天心血来潮,做了一个在线的PHP编辑工具 ,可以方便的练习PHP的基本语法,以及数据库的操作。结尾说可能会做一个Python版的在线编辑工具,那么说到做到。今天就写了个Python版的,供身边的同学以及初学者练习和使用。

看过昨天的那篇博客的应该不难理解,这个工具背后的工作原理。

上传源码,运行脚本,反馈结果。

与之相比,这次的代码稍有些不同,毕竟昨天的是纯PHP代码之间的处理,而今天则是在PHP和Python之间的耦合。所以需要额外的增加一点处理。

在编码之前,应该先把PHP环境设置一下。具体来说就是修改一下php.ini文件。

这里说的原理,从狭义上来讲只是单纯的对于使用PHP调用Python代码。

一般来说有这么两种方式:

这两个方法各有利弊,接下来将简单的介绍一下这两个函数的使用方法,至于怎么选择,按照自己的需求进行设置即可。

system函数本身具有打印命令执行输出的功能,也就是说,程序中的输出可在PHP页面中显示。

如果程序成功执行,则system的返回值为程序输出的最后一行,如果执行失败,返回false。

第二个参数是可选的,用来得到命令执行后的状态码,0表示成功调用外部程序,1表示调用失败。

exec ()函数与system()类似,也执行给定的命令,但不输出结果,而是返回结果的最后一行。

虽然它只返回命令结果的最后一行,但用第二个参数array 可以得到完整的结果,方法是把结果逐行追加到array的结尾处。

另外需要注意的是:只有指定了第二个参数时,才可以用第三个参数,用来取得命令执行的状态码。

  • 获取用户输入Python源码,然后上传到服务器上的temp.py文件。
  • 通过PHP调用外部的Python代码,执行相关脚本。
  • 前台通过ajax方式请求代码运行结果,并显示在result页面块上。

这里的调用外部代码需要的功能很简单,所以就选择了system函数啦。

echo "不好意思,代码运行出错啦。\n\n\n您的语法有问题哟:\n请检查一下标点符号,代码缩进,单词拼写什么的吧!";

temp.py说白了就是个临时文件,所以每次代码被运行的时候都会被更新一遍。所以里面的内容不重要了。下面给出我测试过一次之后的temp.py的文件内容。


下面就是激动人心的测试界面啦。

点击左侧上方的“Python代码”的时候,会给出一点提示信息。如:

此在线编辑工具可以方便的进行Python脚本的编写。只要是符合正规的Python语法的,都是可以的。

当编写的Python脚本比较的耗时的时候,前台需要给出提示信息,并进行等待。所以最好的方式就是显示一条“程序正在运行中··· ···”,这样既能给用户更好的体验,也能体现更加人性化的设计。

上面全是介绍,代码正常运行的结果。但是很多时候我们并不能一下子就编写出bug free 的代码,所以适时的给出点提示是个不错的选择。但是这里给的建议就是自主发现错误,亲自手动的检查自己的代码,更能养成规范的代码编写习惯。

回顾一下,本次的实验核心就在于PHP调用外部程序的两个简单的方式。虽然各有利弊,但是找到合适的场景进行选择的话,还是能取得不错的效果的。

其实上面的全是些无关痛痒的话题啦。真正有用的是整合起来,PHP在网站开发上面有Python不可比拟的优势(虽然Python写起网站来一点也不虚)。但是Python的灵活又是PHP不可替代的。

通过今天这个测试,也不难想到了吧。倘若能恰当的将这两者进行整合,想必一定能做出一个既优雅,又高效的系统。

最后,我把这个工具上传到了自己的服务器上了。如果有想练习PHP语法,Python语法的意愿,可以和我取得联系。

联系方式在左侧的友情链接处可以找到。(^__^) 嘻嘻……

}

京东上的所有商品信息、客户评价、商品咨询、网友讨论等内容,是京东重要的经营资源,未经许可,禁止非法转载使用。

注:本站商品信息均来自于合作方,其真实性、准确性和合法性由信息拥有者(合作方)负责。本站不提供任何保证,并不承担任何法律责任。


京东价:京东价为商品的销售价,是您最终决定是否购买商品的依据。

划线价:商品展示的划横线价格为参考价,并非原价,该价格可能是品牌专柜标价、商品吊牌价或由品牌供应商提供的正品零售价(如厂商指导价、建议零售价等)或其他真实有依据的价格;由于地区、时间的差异性和市场行情波动,品牌专柜标价、商品吊牌价等可能会与您购物时展示的不一致,该价格仅供您参考。

折扣:如无特殊说明,折扣指销售商在原价、或划线价(如品牌专柜标价、商品吊牌价、厂商指导价、厂商建议零售价)等某一价格基础上计算出的优惠比例或优惠金额;如有疑问,您可在购买前联系销售商进行咨询。

异常问题:商品促销信息以商品详情页“促销”栏中的信息为准;商品的具体售价以订单结算页价格为准;如您发现活动商品售价或促销信息有异常,建议购买前先联系销售商咨询。

}

今天是周五下午,你的新版本已经发布好几天了。你礼拜一开始就感到自豪和无事一身轻,但你的自豪感正在随着时间的流逝慢慢减少。发布这样一个没有bug的版本耗费巨大的精力。事实上,在发布日期你有信心认为未来几周将会很安静,因为用户不应该会有别的需求。

当然,它完美得让你难以相信:不久后你的第一个错误报告产生了。第一个错误报告只是无关痛痒的东西,一个新的对话框出现了小小的拼写错误。接着,几个小错误接踵而至,你快速修复它们并存储到存储库。

紧接着,作为每个开发人员的噩梦,最重要的系统部件报告了一个错误。你疯狂地查看代码,即使你知道它的内存内容。代码分支怎么可能在这种情况下执行了?!代码一定出错了。

快速定位到bug处,但你还是想不通这是怎么发生的。你甚至不能在测试环境中重现场景。要是有更多的失败调试消息……

如果你一直在写有试用期的软件,你会意识到这种情况。你感到很沮丧,不管花费了多少精力,软件究竟还是又一次不能用了。不要担心了,已经发生了。

这是故事的一部分,在这里,我找到了为你一劳永逸地解决这个问题的方法。但很不幸,我不认为它会存在。

一个不争的事实是,所有的软件都有bug。然而,这并不意味着我们应该放弃、不追求完美。它只是意味着如果略微改变对现实的看法,我们将得到更好的服务。我们应该这样编写软件,好像我们正在计划防范性措施。我们应该编写软件防范例程,比如设置陷阱,静默捕捉不可避免的bug。

用来描述这种风格***的术语是“防御性编程”。***的描述并不完全符合我的想法,但这是一个好的起点:

一种防护性设计旨在确保软件一部分的持续性功能,而不管软件的不可预见性说法。这个想法可以被看做具有减少或消除墨菲定律所产生影响的前景。防御性编程技术尤其被应用尤其当一段软件代码可能被恶意或无意滥用继而产生灾难性影响。

我真正要谈论的是下列指南的结合:

  • 创建可执行文档更可取[1]

这些指南是至关重要的,它们确保我们的代码以及让你头脑清醒以避免常规错误的发生。记住,从我们的角度来看,编写的代码或多或少会有bug。

我们需要记住以上的指南来帮助我们快速找到bug。很多时候发现bug是最难的环节。因此,让我们优化定位bug的算法,而不是投身于彻底预防错误这个不可能的任务。

让我们在下面的指南中更深入地看一些有用的工具。我们将使用Python作为语言进行演示,但大多数语言都有非常相似的工具。

假设我们有一个函数,它能将从用户和规范化数据获取的值指定在0到1之间,也可以搭配一个新的小部件方便以后使用。

感谢在reddit更新文章中发现一个bug的评论!

接下来证明它确实会将我们给的范围转化到[0 - 1]区间内:

好了,这是一个很短的测试,但是它似乎起作用了。我们通过一系列的“真实”的数字和把它规范到区间(0 - 1)内。

记住上述指导方针,让我们找到这个函数中存在的错误。代码中隐含的假设是什么?

经过仔细检查,上面的代码中存在相当多的假设。不幸的是,浏览代码时它们不会立刻明显地表现出来。要是我们能使这些假设更明晰……

声明语句在单元测试中相当常见。事实上,Python为单元测试定制了大量的声明语句。然而,没有任何理由简单地将这个有用的工具用在测试环境中。

常规代码里的声明语句非常有用。这些声明语句需要一个表达式,如果表达式为false,则会抛出一个Asserti异常以及一个可选消息。

例如,我们上面的函数总是返回一个在(0 - 1)之间的值。不幸的是,我们的假设更多地显示程序并没有达到预期:

你可以想象,这种情况很容易被忽视很长一段时间,这个返回值会在整个代码中传播。这正是本文开头的故事:错误的准确类型不可能被发现而导致的可悲故事。

我们可以试着考虑到用户可能输入的每个值并妥善处理它。事实上,这是正确的做法,但不能保证我们不会遗漏数据。我们已经承认了程序员会犯错这一事实。

幸运的是,既然我们已经承认我们犯错误,我们可以使用声明语句在以后重构代码。

我们添加了一些声明语句,如果返回值不在预期范围之内,它们会发出警告。让我们来看看这些声明如何改变我们的测试代码:

这种方法有以下几个优点:

  • 在问题的根源设置警告器
  • 包含有价值的关于“无效”参数的调试信息

1. 作为一种可执行文档

典型文档通常有几种不同的特点如内联或块注释,文档字符串,以及sphinx。这些特点具有特定目的,它们几乎都是软件开发的关键。不幸的是,他们都遇到了同样的问题。它们可以很快地与快速变化的代码和需求保持同步。这会导致开发人员不信任原始文档。

将声明文档化有不同的目的。他们清晰而简明地描述应用程序在运行时的预期状态。此外,如果我们改变我们的假设时没有修改声明以匹配新的行为,应用程序就会崩溃。

声明语句***随其他变动一起更新。因此声明比非可执行文档更值得信赖。此外,断言还具有许多诸如评论,文档字符串等的益处。

值得指出的是,有另一种被称为doctest形式的可执行文档,在Python文件系统中十分常见。这些测试/文档可能有些丑陋,但他们的关键特性是他们近乎于代码,就像声明一样。

2. 在问题的根源设置警告器

我们都有过类似的经历,你花费几个小时调试bug,最后意识到真正的bug一开始就不在开始调试的代码中(见5个为什么)。也许导致bug的原因根本就不是你第一次注意到的错误行为。

例如,你在系统中发现一个字节字符串,但你认为内部都是Unicode字符串。可能需要很长时间才能找到格式转换功能在哪里失效了。这处境真是令人沮丧。如果更早发现了bug就好了,或者至少有更多的调试信息。

声明语句不会阻值这种情况的出现,但它们确实提供了一个改善的机会。上面的声明将警告我们:这时该函数不服从它们之间的约定,没有返回一个(0 - 1)区间内的值。如果我们随后发现其他代码的范围无效,它会给我们一个有价值的线索。我们将会知道这个函数没有按照既定要求执行。这一条线索可以节省时间,因为它可以避免从症状的根源追溯其发生原因。

3. 包含有价值的关于“无效”参数的调试信息

请注意到我们声明语句还包括输入参数的信息。当用户使用那些我们没有访问权的数据而遇到错误时,这些信息将具有无可估量的价值。此外,当用户很难讲清楚错误发生时的情况时,调试信息尤其有用。因此,这些声明语句可以防止你无意把bug标记为“不可重现”状态。

输入参数信息也有其他一些微妙的好处:

  • 显示一条关于用户正在使用何种类型数据的无效假设
  • 解释我们在文档中期望使用何种类型数据的疏忽
  • 使潜在的无法执行的新实例暴露出来

我们已经编写好可以提供大量好处的声明语句,但有时候它们表现得并不有效。通常,有以下几个缺点:

通常情况下,出于对技术和显示情况的考虑,声明语句并不符合代码编写规范。它们仅仅在调试常量为true时**。然而,常量默认为true,这意味着你的代码很有可能在发布时处于调试模式。

如果你的应用程序运行在少量额外逻辑频繁引起注意的环境中,就需要注意一下了。关闭调试模式的唯一方法是以-O 选项运行Python解释器。

很容易过度使用声明语句而使代码很快变得难以阅读。这会使你的代码混入很多噪声,而实际功能被一系列的错误检查淹没。下面的代码是一个过度使用声明语句的示例,让我们看看想明白代码到底干什么有多困难。

假设你认定某些错误绝不会发生,就该保守地使用声明语句。不要过分使用声明语句去检查无效输入。

这没有硬性规定,每个开发人员可能会不同程度地使用它。请尝试采用你自己的标准,包括一些在你的开发中定义的风格指南。

你应该有自己的风格吧?

当然,也要记得Python支持duck-type类型(译注:鸭子类型,动态类型的一种),因此不要因为过度使用是明语句检查数据类型而把这种功能淹没了。

我发现的一个有用的技术是将所有捕捉到的Asserti异常优先级设置为最高,并结合另一个有用的技术进行处理。

日志的使用类似于声明语句。它可以提供运行时调试信息以改进你的可执行文件。然而,日志并不完全像声明。它有一些额外的好处。

1. 更出色的控制粒度

Python的日志模块包含面非常广泛,并且可定制。你可以把消息发送到几个不同的级层,每一层可以开启或关闭。因此,你***考虑一些更为严重的情况,以帮助你在这个日志级层编码。

记住,这里的情况与声明语句无关,它们仅仅依赖于调试模式。

日志记录允许你随地浏览日志级别。常见情况是一个配置文件,环境变量或是数据库。这种灵活性允许你控制日志信息显示量,而不必重新运行或重新部署应用程序。

相比之下,Python不允许为声明语句动态分配调试常量。因此,你无法在不重新运行应用程序的前提下打开声明开关。

决定你的防御编码策略时,值得考虑一下它。如果你使用了一个“可代替”的工具,动态日志控制就显得尤其重要,如PyInstaller分配机制。

3. 静默保存回溯信息

发生错误时实时保存回溯和调试信息相当重要,智能日志可以自动完成保存过程。

这个概念是道格·赫尔曼在一个著名的的异常处理程序中提出的:

自动调用 log.exception 会带给我们更多异常消息。然后,我们可以配置日志记录器将回溯和异常消息分别存储到不同日志文件,方便以后在没有正常消息和警告的情况下检查程序。

如果在运行代码中启用这个异常文件,它可能包含非常有用的调试信息。这为分析日志数据创造了很多令人兴奋的可能性:

  • 用户会尝试不同排列的特性,而这些特性我们从来没有测试甚至没有考虑过,这可能导致新特性变多,以使常见实例更加易用。
  • 找到由于用户误解应用程序的工作方式而犯的类似错误,这可能为编制更好的用户文档作出相当贡献。

你还可以使用声明语句配合日志记录。例如,你可以在默认调试模式下运行应用程序,然后捕捉Asserti并将日志记录到另一个文件。这可能为数据挖掘创造更多的可能性,如发现一个假定在平台中运行的环境。

这只是几个使用日志的防御性编程的例子。事实上,你可以使用日志创建低保真环境以解决各种各样的问题,另一篇文章有相关介绍。[2]

日志和声明有许多相同的缺点。然而,日志的额外的灵活性伴随着额外的负担需要考虑。

1. 难度管理级别一致性

使用日志记录最大的困难是相同级别的一组日志始终贯穿整个代码库。这可以归为主观的命名问题,这两个问题仅在计算机科学中存在。***的解决方案是随代码风格一同提交指南。随后添加日志消息时,会有一些具体项目的文档供新手参考。

2. 设置多个日志记录器

日志模块是非常灵活的,但它是有代价的。日志配置将变得很复杂。考虑采用以下策略:

  • 调试级别的消息保存在一个名为.debug的隐藏文件中
  • 信息、警告和错误级别消息保存在stderr文件中
  • 关键和异常级别的消息以GUI消息框形式弹出

这是一个很好的起点。同时,Python的文档包括几个绝对值得一读的优秀日志记录策略,决定你的设置之前,***读一下。

记住,当你调试bug时,日志可以表现出相当大的优势。因此,一定要花时间去学习日志系统,确保您的配置设计得足够仔细。即使简单的应用程序也应该有一套良好的、精心设计的日志记录策略。

保护自己免遭bug烦扰的最后一种方法是不要忽略过去的bug。过去的bug倾向于长时间潜伏在代码中,这通常是由于有人不理解为什么一些代码要以一种特殊的方式编写,继而删除了它们以”清洁“系统。

这就引出了另一个场景,创建另一种版本的可执行文件相当有用。因此,当一个毫无戒心的开发人员修改一些代码或者删除一些正在运行的代码来警告他们的错误,就没有什么灾难性后果了。

人们通常在测试驱动开发的背景下会遇到单元测试的情况。这是一个很好的概念,但往往在实践中有点过于乐观因而很难遵循(阅读材料: customer wants code now)。相关的讨论在另一个博客帖子中。

我想讨论的是如何使用单元测试使你从今后和过去的bug中解脱出来。我认为它将TDD测试和一些更有成效的能够减少前期时间成本的概念颠倒了。

我建议你修复bug后写一份测试报告 (更多讨论)

这里有几个可能不会立即表现出来的目的:

改善bug修复的文档化

要确保你提交的信息包含帮助你修复bug的内容,但不要就此止步。很可能你提交的信息和评论缺乏类似于针对以下提问的解决方案:

  • 具体是哪种情况导致了这个bug?

这就是一个优秀的单元测试开始起作用的时候了。你已经知道如何测试bug。(你之前做过测试,对吧?)所以,描述你测试的场景吧,让每个人都受益于你的努力。

单元测试是一个出色的环节,在这里所有的bug将展现出来,并且它会提供修复bug的文档。你不仅可以解释如何以及为什么你要修复它,而且你测试它的方式也能细细道来。如果bug又出现了,那么这条信息将会非常有价值。

不要忘记,一次正常运行的测试可以为bug的定位线索(让你知道bug没有在这里发生)。

2. 能经得住时间考验的重复bug排除编码

特定bug不知不觉地嵌入代码库并不是稀罕事。由于需求变化,重构错误,或其他部分的变动,这是可能发生的。可以通过为一个特定的bug修复编写一个单元测试回归,且要记得运行你的测试代码。将单元测试看做是从稍后又会遭遇bug的麻烦中解放出来的机会。

单元测试的主要缺点是,你很容易忘记运行它们。这只是一个无法从代码定位的测试副产品。通过类似的实践,像持续集成化使用Travis CI 和自动化测试,这个不足可以得到弥补。

另一个缺点是单元测试通常只在自己的测试环境中执行,这是一个没有真实用户的模拟环境。

这种风格的发展很难分类,不幸的是没有任何既定的规则说明何时使用它。所以,我强烈建议你牢牢记住指南。

该指南将导致你的观念发生微妙的变化。观念的变化相当重要,而不是工具和机制本身。最终你会因为过度使用声明或日志记录犯一些错误,进而开始形成自己的风格。同时,每一个项目的需求都不同,所以有必要学会所有的工具,进而以需要的解决方式混合使用它们。

[1]可执行文件是一个术语,有时用来描述测试框架doctests。术语“Literate testing”用来描述这一概念。

[2]你甚至可以使用日志来构建自己的分析工具。登录到网络或Dropbox文件,每次将使用一个特性。然后,后台将有一个shell脚本用来收集这些文件。现在你有基于简单文本格式的大量使用信息,它包含开放的大量数据分析的可能性,你可以用来帮助你的用户。

}

我要回帖

更多关于 python编程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信