• 第 3 章 编程的艺术


    目录

    3.1学习编程的不同方法

    3.2编辑-运行-修正(还有保存)

    3.3编程文化

    3.4编程策略

    3.5编程过程

    本章将概述程序员是如何完成他们的任务的。如果你已经安装了Python,而且想立即编写生物信息学中的实用程序,可以跳过本章直接阅读第4章

    初进生物学实验室的人对各种试管仪器会有一种莫名的崇拜,与之类似,刚刚接触编程的初学者也会对程序员的世界充满了好奇,认为它们如同一个充满了怪异术语和高深技能的神秘黑盒子。为了让读者尽快融入这个大家庭,我会对重要的知识和技能进行简短的介绍,每个程序员都要学习并使用到这些内容。其中有两个是最为重要的,一个是优秀程序员在实践过程中所使用的编程策略,另一个则是如何找到在编程过程中遇到的各种问题的答案。通过一些简单的描述性实例的学习,我们将总结出程序员寻找问题解决方案的方法。附录A中罗列了一些非常好的Python和生物信息学的资源,在解决实践过程中遇到的问题时,它们会对你有所帮助的。

    3.1学习编程的不同方法

    学习编程的最佳方法是什么?答案取决于你要完成的任务。有许多引领你入⻔的方法,比如:

    • •参加各种类型的学习班
    • •阅读像本书这样的教程性的书籍
    • •死啃编程手册
    • •拜其他的程序员为师
    • •研究你所需要的一个程序
    • •尝试上述几种或所有方法,直到对编程能够驾轻就熟为止

    答案还取决于你打算如何学习编程。有人喜欢参加培训班,因为培训班会把知识点整理得有条有理,同时授课老师也会解答学生的各种问题。另外一些人则可能会更加喜欢自学。

    不管是那种学习编程的方法,有些东西却是共通的。如果以前你从未写过程序,下面几个小节的内容是你首先必须要了解的。

    3.2编辑-运行-修正(还有保存)

    就像跳舞、弹琴、烹饪或者其他家庭中的活动一样,学习编程需要你实际动手去做,这是最重要的一点。你可以阅读源码,但如果不动手去编写、调试程序,你永远也无法真正编写出程序。

    当学习Python语言编程时,你要弄明白Python是如何工作的,就像在接下来的几章中你将看到的那样。此外,你还要学习大量的程序实例。在接下来几章的最后有一些练习题供你练手,此时你要尝试编写自己的程序。只有这样的实战练习才能使你成⻓为一个真正的程序员。

    为了帮助你写出第一个程序,同时弄明白它的工作原理,我会对一些内容进行简要的概述,这些在编写程序过程中都是至关重要的。

    你用计算机做什么?程序员使用计算机时,大部分工作都无外乎在编辑器中编写或修正程序,然后运行程序并检查它的运行状态,再根据运行状态回去检查和修正程序,如此循环往复。对于一个程序员来说,通常一半以上的时间都用在了编辑程序上。

    3.2.1保存和备份

    即使你只写了几行代码,也一定要保存它,请牢记这一点。实际上,在编辑代码的过程中,应该有定期保存程序每一个版本的观念。这样在进行了大量编辑后,即使计算机崩溃了,你也不会丢失数个小时的劳动成果。此外,要确保在另一个磁盘上对你的工作进行了备份。如果你的硬盘损坏了,上面的所有数据都会丢失。所以,对你的工作进行定期(每日)备份至关重要,可以备份到磁带、软盘、ZIP磁盘片、另一个硬盘、光盘等其他媒介上。不管怎样,当你的磁盘损坏时,因为备份的存在,你的工作内容将不会全部丢失。除了备份硬盘,还应记住,要对你的程序进行定期保存,保留它的每一个版本。这样,当你需要回退到程序的历史版本时,你可以轻松做到。

    还有一点,一定要确保你的备份确实可以使用。举个例子,如果你把数据备份到了磁带上,每次备份一段时间后,都要尝试从磁带上恢复文件,来确保软件以及磁带本身都可以正常使用。以防系统崩溃,你可能要定期把程序打印出来(“白纸黑字”),这样会多一份保险。最后一点,比较好的一个策略是不要把鸡蛋放在一个篮子里,把你的备份放在一个远离计算机的地方,这样即使遇到火灾或者其他灾难,你仍有备份可用。

    3.2.2错误信息

    修复错误是编写程序过程中的基本步骤。在编辑完一个程序后,接下来你要做的就是运行它,看看程序能否正常工作。多数情况下,你会发现有一些像忘记添加分号这样的拼写错误。结果可想而知,因为程序中存在语法错误,运行时你会看到系统给出的各种错误信息。这时,你将不得不仔细查看错误信息,去重新编辑程序以消除这些讨厌的错误。

    有时,这些错误信息是比较隐秘的。这种错误导致的结果就是,Python解释器很难精确定位错误所在,它仅知道某个地方出错了,也仅此而已。所以,它会猜测问题的根源所在,在这个过程中,它可能会给出一些与错误本身无关的信息。

    牢记一点,在处理错误信息时,要对后面的视而不⻅,只关注第一个或前两个错误信息即可。修复这一两个最先出现的问题,然后尝试重新运行一次程序。错误信息通常都比较冗⻓,甚至可能有数⻚之多。除了第一个错误,把其他的都忽略掉吧!还有一点需要注意,第一个错误信息中给出的代码行号通常都是正确的,有时它会有一行之差,但不会偏差太大。稍后,我们会练习制作错误并解读错误信息。

    3.2.3调试

    也许你编写了一个程序,Python解释器可以正常运行它而没有任何报错。然而你却发现,程序并没有像你期望的那样工作。现在,你需要回过头来,去查阅程序代码,尝试找出问题所在。

    也许你仅仅是犯了一个低级错误,比如,本应该使用减法的地方你却用了加法。也有可能你误解了文档中的内容,以错误的方式编写了程序(重新阅读文档吧)。还有可能你对要完成的任务考虑不周、计划不足(想清楚具体策略后重新编写相应部分的代码吧)。有时你并不能找到问题所在,那就四处求助吧(可以尝试在新闻组存档和FAQs中搜索一下,或者向同事求助)。

    对于那些难于发现的错误,有一个叫做调试器的程序专⻔对付它们。调试器允许你一步一步运行程序,看看在运行过程中到底发生了什么。(第6章中对Python的调试器进行了深入讲解。)

    当然,还有其他工具或者技术可供选用。比如,在程序中加入print语句,把那些中间值或中间结果输出出来,这也可以帮助你检查程序。此外,也有专⻔的程序,在程序运行过程中它会对其进行观察,然后输出相应的报告,比如,告诉你程序的哪一部分消耗的时间最多。所有这些工具,以及其他类似的工具,对于编程来说都是必需的,一定要学会如何去使用它们。

    3.3编程文化

    编程是解决问题的实践,它是一个重复、循序渐进的过程。单打独斗未尝不可,但它通常是一个群体性的活动(让很多初学者惊讶吧)。它需要你培养特定的解决问题的技能,还要求你学习一些新的工具。编程有时难以捉摸,会让人厌恶至极。另一方面,对于那些有能力的程序员来说,当成功编写出程序后,也会有极大的成就感。

    计算机程序无所不是,从一无是处,到给人美学及理智上的刺激,再到催生全新的知识。它们是如此美丽!(它们也有可能是破坏性的、愚蠢的、糊涂的甚至狠毒的,毕竟是人类创造了它们。)编写程序是一个重复、循序渐进的构建过程,从开始时的只砖片瓦,到最后的高楼大厦,看到这样的过程,谁能不由衷自豪呢?对于初学者来说,编程中的这种循序渐进的过程,简直就是一步一步学习语言的过程的缩影。

    自从20世纪中期人们开始编写并积累程序,编程文化就在不断形成。慢慢地,我们积累出了坚固的编程文化底蕴。程序会反映出其他程序对它的影响,这就是文化氛围的力量,而程序员菜⻦也会从中受益良多。

    3.3.1开源程序

    编程越来越重要,随之就产生了经济价值,导致的后果便是,好多程序的源代码被隐藏了起来,以此来保存它的商业价值、提升竞争力。

    然而,对于那些最好、最有用的程序的源代码来说,每个人都可以免费获取进行查阅。可以免费获取的源代码通常被称作开源。(对于开源程序代码来说,有各种各样的版权附加其上,但这些版权都允许任何人去查阅源代码。)开源运动对待程序源代码的态度,和科学家发表研究结果的态度非常类似:公布于众以供他人检查和讨论。

    对于程序员菜⻦来说,这些程序的源代码非常有用,因为从中可以学习到专业程序员是如何编写代码的。开源的程序包括Python解释器和大量的Python代码、Linux操作系统、

    Apache网络服务器、Netscape网⻚浏览器、sendmail邮件传输代理等等。

    3.4编程策略

    为了让初学者对编程过程有一个直观的认识,通过几个教学性的实例研究,让我们看看程序员高手是如何思考来解决问题的。

    假设你从测序实验室中拿到了一大批DNA序列,现在想对其中的调控元件进行计数。如果你是一个专业的生物信息学程序员,你会怎么做?有两种可能的解决策略:找一个现成的程序,或者自己动手写一个。

    非常有可能已经有这样一个程序,正好是你需要的,可以正常运行而且是免费的。大多数时候,在网络上都可以找到你所需要的程序,从而避免了重复发明轮子的代价。这就是性价比最高的编程——最小的工作量换来了最大的成果。图书馆里呆一天,胜过实验室里忙半年,(他山之石可以攻玉)这是实验工作者的经典谚语。

    时刻注意收集整理可用的程序,这是编程艺术中很重要的一部分。之后,你就可以使用合适的程序来完成自己的工作,或者对已有的程序进行修改来满足自己的需要。当然,还是要注意版权声明的,但是大部分都是免费的,尤其是针对学术和非营利机构。大部分的Python模块都有版权,虽然有一定的限制,但都允许你使用并修改它。版权的细节可以在Python网站上可以,也会和特定的模块附在一起。

    如何找到这些已经存在且免费的很棒的程序呢?Python社区已经把这样的程序代码收集整理起来,放在了Python综合典藏网(pypi)上,网址是https://pypi.org/。去随便看看吧,你会发现它是按照主题进行整理的,所以可以快速查找网络、统计或图形等主题的程序。在我们这个例子中,你需要查找BioPython模块,它包含了非常多的实用的生物信息学功能模块。所谓模块就是Python代码的集合,你可以在Python程序中轻松地加载并使用它们。

    最有用的代码就是那些模块,它把许多相关的功能整合在了一起。在编写新程序的过程中,这些模块给予我们了极大的灵活性。尽管你仍然不得不去编程,但工作量已经大大减少,只需利用它们拼凑出一个完成的程序即可。还是以寻找调控元件为例,通过搜索你可能会发现一个方便使用的模块,它包含了所有的调控元件,还附带有相应的代码——给定调控元件后可以在DNA序列库中进行查找!而你需要做的仅仅是把这些现成的代码整合起来,给它提供一个DNA库,然后只需要很少的编程你就可以完成任务了。

    还有好多其他地方可以找到现成的代码。你可以使用自己最喜欢的搜索引擎在网上进行搜索,可以通过浏览生物信息学链接合集来寻找程序,也可以在我们提到的新闻组、相关专家等资源那里进行寻找。

    如果你没有找到问题的突破点,并且你也明白,自己写程序需要花费大量的时间,你就会想去图书馆的文献中自己查找一下,或者在图书管理员的帮助下进行查找。你可以在MEDLINE数据库中查找调控元件相关的文章,这些文章中通常会刊登有作者使用的代码(一个用Python语言编写的程序)。你也可以在会议记录、书籍和期刊中进行查找。会议和商品展销会都是不错的地方,可以去逛逛,在那里你可以和其他人进行交流、向他们进行提问。

    多数情况下,你都会如愿以偿,尽管你也有一定的付出,但是你为自己和实验室节省了数天、数周甚至数月的时间。

    但是,针对修改现成的代码还有一句警告:有时修改现成的代码可能会比从头写一个完整的程序更加困难,这取决于有多少代码需要修改。为什么会这样呢?不同的程序有不同的作者,要弄明白程序各个部分的作用,有时候还是比较困难的。如果你都不理解程序第一部分使用的方法,修改又从何谈起?(稍后,我们会详谈如何编写可读的代码,以及在代码中添加注释的重要性。)这个因素本身在编程代价中就占了很大一部分。许多程序阅读、理解起来非常困难,因此也难以维护。而基于种种原因,测试这样的程序也会非常困难。要确保你的修改能够正常工作,有时会花费大量的时间与精力。

    好吧,如果说你已经用三天的时间来寻找现成的程序,最终却一无所获,(也许有一个现成的程序,但是需要三万美元才能买到,这已经超出了你的预算;同时你身边的程序员专家忙得不可开交,也没有时间来给你写程序。)那你将不得不自己动手写一个程序了。

    如何从头写一个在DNA序列中计算调控元件数目的程序呢?听我慢慢道来。

    3.5编程过程

    现在,你被指派去写一个在DNA序列中计算调控元件数目的程序。如果以前从未编写过程序,那你很可能会毫无头绪。为了写出这个程序,我们先讨论一下你需要知道的内容。

    我们将逐步进行,这里是对这些步骤的简要概述:

    1. 确定必需的输入,比如用户提供的数据或信息。
    2. 对程序进行整体构思,包括程序计算输出结果的基本方法——算法。
    3. 决定结果的输出形式;比如,输出到文件,或者进行图形化展示。
    4. 通过添加更多的细节改善整体构思。
    5. 编写Python程序代码。

    对于更短或者更⻓的程序来说,这些步骤可能有所不同,但对于你的大多数编程来说,这就是最基本的步骤。

    3.5.1构思阶段

    首先,对于程序如何工作,你需要酝酿一个计划。这是对于程序的总体构思,而且是一个关键的步骤,这样的构思通常在实际开始编写程序之前就要完成。编程常和厨房中的⻝谱相类比,毕竟它们都是完成某项任务的特定操作指南。比如,你要知道程序需要什么样的输入和输出。在我们这个例子中,输入的是新的DNA序列。接下来,你需要一个策略,就是程序如何对输入进行计算处理得到我们想要的输出。

    在我们这个例子中,程序首先要从用户那里收集信息:也就是说,DNA序列在哪里?(这样的信息可以是存储DNA序列文件的文件名。)程序需要用户键入数据文件的文件名,可能是在计算机屏幕上进行输入,有可能是通过网⻚进行提交。然后,程序需要检查一下文件是否存在(有时文件并不存在,这时有发生,比如用户拼错了文件名,此时程序要给出警示),然后打开这个文件,在进行计算处理之前读入DNA序列。

    该步骤虽然简单,但仍值得进行一定的注解。你可以直接把DNA序列放在程序代码中,这样就不用去写这部分代码了。但是把程序设计成读入DNA序列还是非常有用的,因为当你拿到新的DNA序列时就不用每次都重写程序了。这种想法非常简单,甚至是显而易⻅的,但却非常有效。

    程序用来处理的数据叫做输入。输入可以有多种来源,如文件、其他程序、运行程序的用户、网站上填写的表单、电子邮件信息等。多数程序都读入某种形式的输入,但也有程序没有输入。

    让我们把调控元件添加到实际的程序代码中吧。就像我们处理DNA序列一样,你可以从文件中读入调控元件;文件中存储的是调控元件的列表,这样程序就可以查找不同的调控元件了。但是,在这种情况下,我们要使用的调控元件列表并不会改变,那为什么还要麻烦用户输入这样一个文件的文件名呢?

    既然有了DNA序列和调控元件列表,我们就要概括地决定程序如何在DNA序列中查找每一个调控元件。这一步显然是最为关键的一步,它将一步定乾坤。比如,如果程序的运行速度是一个重要的考虑因素,你就要让程序运行的足够快。

    在这个工作中,此处的问题就是要选择正确的算法。算法就是处理问题的构思(⻢上我就会对此进行详述)。比如,你打算交替读入调控元件,在读入下一个调控元件之前,从头到尾在DNA中对当前元件进行查找。或者也有可能,你打算对DNA序列只进行一次从头到尾的读取,然后在DNA序列的每一个位置对每一个调控元件都进行一次查找,看看此处是否存在该调控元件。这两种方法有没有优劣之别?是否可以先对调控元件列表进行排序,这样查找就可以更快一些?此时,我们会说算法的选择至关重要。

    构思的最后部分就是把结果以某种形式输出出来。也许你想把结果展示在网⻚上,或者在计算机屏幕上输出简单的列表,或者保存到一个可供打印的文件中,也可能是上述所有形式。在这个阶段,你可能需要让用户提供一个文件名来保存输出。

    这就是如何展示结果的问题,这也确实是一个至关重要的问题。最理想的解决办法就是以某种方式把结果展示出来,这种方式使得用户仅需一瞥就可抓住计算过程中的重要特性。你可以使用图形、颜色、地图,甚至可以把异常结果用跳跃的小球标示出来,总之有很多方法。如果一个程序的输出结果很难解读,显然这并不是一个好的程序。事实上,让重要结果很难寻找或理解的输出,会彻底抹杀掉你在编写这个优雅程序过程中的所有付出。现在说的已经足够多了。

    有非常多的策略,程序员可以使用这些策略来帮助他们进行好的整体构思。通常情况下,除了最小的程序外,所有的程序都会被分割成多个微小但却互相关联的部分。(在后续章节的学习中,我们会看到好多这样的例子。)每个部分是什么?它们之间如何进行关联?软件工程这个领域处理的就是这样的问题。此时此刻,我只想指出它们是非常重要的,提及一些程序员实现构思的途径。

    有许多构思的方法,每种方法都有各自的拥护者。最好的办法就是了解一下有哪些可用的方法,并在处理任务时使用最合适的方法。比如,在本书中,我会教授一种叫做命令式编程的编程范式,它的理念是把一个问题分割成相互配合的程序或子程序(参看第6),这就是所谓的结构化设计。另外一种流行的编程范式叫做面向对象编程,Python也支持这种范式。

    如果你在一个大的项目中和一堆程序员共事,构思阶段可能会非常正式,甚至可能会由其他人来完成,而不是由程序员自己来完成。另一方面,你可能会发现有些程序员一上来就编写程序,他们会边写程序边制定计划。没有一种适用于所有程序员的方法。(萝卜白菜,各有所爱。)但不管你怎样实现它,作为初学者,在开始编写代码之前,你最好还是要在头脑中有一定的构思。

    3.5.2算法

    算法就是构思、计划,计算机程序的计算过程。(如果不使用正式的数学语言,这确实是一个难以定义的术语,不过此处也算是一个合理的定义。)一个算法要通过使用特定的计算机语言编程来实现,但算法本身是计算的思路。使用伪代码可以对算法进行很好的表述,它不是一个真正的计算机程序,但却展示了程序的思路。

    大部分程序都只做简单的事情。它们从用户那里获取文件名,打开文件并读入数据,然后进行简单的计算后把结果展示出来。此处你将学习算法的类型。

    不管怎样,算法学都是一⻔深奥、成果卓越的学科,对生物信息学也产生了深远的影响。通过算法,可以找到新的分析生物学数据的方法,发现新的科学成果。在生物学中,确实有不少这样的问题,这些问题的解决必须要等待新算法的发明。

    算法学中有许多巧妙的技术。作为一个程序员菜⻦,现在还没有必要为这些担心。在这个阶段,本章作为编程方面的入⻔教程中的导言章节,对算法方法进行深入讲解并不明智。你的首要任务就是学习如何用某种编程语言进行编程。当然,如果对算法念念不忘,你可以去学习相关的技术。买一本像样的教科书放在身边作为参考书,对一个严谨的程序员来说绝对是值得的。(参看附录A)。

    在现在这个例子中,就是在DNA序列中对调控元件进行计数的这个例子,我给出了一种策略:交替读取调控元件,在处理下一个调控元件之前,在DNA序列中进行从头到尾的查找。其他算法也是可以的。事实上,这是字符串匹配这个普遍性问题的一个实例。字符串匹配在生物信息学中最重要的一部分,而对它的研究已经催生了许多巧妙的算法。

    算法常常和这样的问题或技术一起被提起,并且也有许多资料也可查阅。对于实用主义的程序员来说,最无价的材料莫过于用特定语言写成的算法代码,你可以直接把他们用在自己的程序中。可以从附录A开始,使用其中列出的代码合集和书籍,能够相对轻松的把许多算法技术整合到你的Python代码中。

    3.5.3伪代码和代码

    现在你有了整体构思,包括输入、算法和输出。你该如何把这些想法应用到程序的设计中呢?

    一个常用的实践策略就是从编写伪代码开始。伪代码是一种非正式的程序,其中没有具体的细节,也不需要遵守正式的语法规则。伪代码不会向程序那样实际运行,它的目的仅仅是以一种快速、非正式的方法把程序的整体构思具体化而已。

    举个例子,在实际的Python程序中你可能会写一些叫做子程序的代码(参看第6章)。在这个例子中,子程序会从用户的键入内容中获取答案;这个子程序可能会像这样:

    def getanswer():
        print "Type in answer here :"
        answer = input().rstrip()
        return answer
    

    但是在伪代码中,你可能仅仅需要这样写:

    getanswer
    

    至于细节日后再说。

    对于上面讨论的内容,这里是程序伪代码的一个例子:

    get the name of DNAfile from the user
    
    read in the DNA from the DNAfile
    
    for each regulatory element
        if element is in DNA, then
            add one to the count
    
    print count
    

    3.5.4注释

    注释是Python源代码的一部分,旨在帮助理解程序的所作所为。从#开始到行末的所有内容都被看作注释,会被Python解释器忽略掉。(唯一的例外是大多数Python程序的第一行,是像这样的一行:#!/usr/bin/python;参看第4章中的第4.2.3小节。)

    注释对于保持代码可用是非常重要的。注释通常包括对于程序主要目的和整体构思的讨论、如何使用程序的实例,以及散布程序各处的细节注解,来解释为什么那段代码在这里、它是做什么用的。一般来说,好的程序员会把好的注释作为程序完整的一部分来进行编写。在本书的所有编程实例中,你都会看到注释。

    你的代码不只会被计算机读入,也会被人查阅,这一点非常重要!

    在调试行为异常的程序时,注释往往非常有用。如果无法确认程序出错的地方,你可以尝试选择性地注释掉不同部分的代码。如果你发现当注释掉某一部分代码后问题消失了,就可以把出错范围缩小到你注释掉的那部分代码,当这部分代码足够短时,你就找到问题的所在了。这常常是一个非常有用的调试方法。

    在你把伪代码转换成Python源代码时,也可以使用注释。伪代码不是Python代码,所以对于任何没有注释掉的伪代码,Python解释器都会报错。在伪代码所在行的开头加上#就可以把它注释掉了:

    # get the name of DNAfile from the user
    
    # read in the DNA from the DNAfile
    
    # for each regulatory element
    #    if element is in DNA, then
    #       add one to the count
    
    # print count
    

    在你把伪代码扩展成Python代码时,移除#就可以对Python代码取消注释了。用这种方法时,Python代码和伪代码会混杂在一起,但你可以正常运行或测试Python代码部分,因为Python解释器会把注释行简单地忽略掉。

    你可以把伪代码整个保留在程序中,只需把它们注释掉即可。这种做法会保留下程序构思的提纲,当你或其他人试图阅读或修改代码时它们就会派上用场。

    现在我们已经可以开始进行真正的Python编程了。在第4章中,你会学习Python的语法规则,并开始用Python进行编程。当你开始编程时,牢记首先要对你的程序进行构思,之后才是你将花费大量时间重复去做的事情:编辑程序、运行程序,然后修正程序。

  • 相关阅读:
    Android TabHost(选项卡)
    监控工具之---Prometheus查询持久性(六)
    监控工具之---Prometheus表达式promQL生产中应用(五)
    Grafana Configuration 参数详解(1)
    监控工具之---Prometheus数据可视化Grafana(七)
    监控工具之---Prometheus 安装详解(三)
    监控工具之---Prometheus 配置exporter四)
    Kubernetes容器编排技术---kubectl命令行工具用法详解(三)
    Kubernetes容器编排技术---Kubernetes基于kubeadm安装与配置(二)
    Azure Iaas基础之---创建虚拟机
  • 原文地址:https://www.cnblogs.com/yahengwang/p/9374218.html
Copyright © 2020-2023  润新知