第六章 函数和调试
Posted yahengwang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第六章 函数和调试相关的知识,希望对你有一定的参考价值。
在本章中,你将学习以下两方面的基本知识:
-
- 函数
- 使用Python调试包pdb
函数是程序重要的组成部分,你将在第七章中使用它们,并学习如何使用随机化来模拟DNA突变。Python调试包pdb可以将程序放缓,按步执行找到出错的地方。
1.函数
函数是组织好的代码块,并提供一种方法(函数名称)可以传递一些值进行计算,然后返回结果。程序可以通过调用函数名称来使用函数中的代码,将函数所需的值传递给函数中的代码,然后返回结果,这种方式使用或“调用”通常称为调用函数。Python有许多内建的函数,例如上述使用的print、input、open等等。
1.1 函数的优势
函数可以将程序抽象、模块化以及将代码组织成输入和输出的可管理块来创建大型程序的能力。
假设你需要计算某些内容,例如计算程序中几个地方或不同程序中的分布均值。编写函数后,你就可以在需要使用的地方调用函数,从而是你的程序:
-
- 简短,因为减少了重复代码
- 更容易测试,因为可以单独测试函数
- 易于理解,函数使程序组织更清晰
- 更可靠,因为在重用函数时,代码少,因此出错机会更少
- 开发快,因为你可能已经编写了一些处理基本统计数据的函数,使用时只需调用计算均值的函数而无需再次编写。或者,你找到了一个别人写好的统计库,不需要你编写了。
函数本身也可以调用其它函数,通过编写一组函数,每个函数都可以完成一项或几项工作,然后通过各种方法将它们组合起来形成新的函数,最终结果是形成强大灵活的编程系统。在编程中,将问题分解为可以方便组合的函数集,使可以创建适应不断变化的条件而只需花费最小代价。
所有这些内容的诀窍在于如何将代码分区为函数。函数封装了一些常用的东西,而不是只调用一次;函数应该只做一件事,代码只有一两页。这些不是真正的标准,但可以帮助你将代码划分为适用于函数的可管理块。
1.2 编写函数
让我们看看如何使用函数,然后看看它们是如何定义的。
调用函数,传递数据给函数数,然后接收返回值。例如,假设你想实现一个函数addACGT,给定一些DNA,将“ATCG”添加到DNA的末端并返回新的DNA。在Python中,通常键入函数名称来调用函数,然后是带括号的参数列表。例如,使用参数dna嗲用函数addACGT:
addACGT(dna)
例子6-1演示了该函数,详细说名了原理。
例子6-1 DNA末端加ACGT函数
#!/usr/bin/env python # A program with a subroutine to append ACGT to DNA ################################################################################ # Subroutines for Example 6-1 ################################################################################ # Here is the definition for subroutine "addACGT" def addACGT(dna): dna += ‘ACGT‘ return dna ################################################################################ # The original DNA dna = ‘CGACGTCTTCTCAGGCGA‘ # The call to the subroutine "addACGT". # The argument being passed in is dna; the result is saved in longer_dna longer_dna = addACGT(dna) print("I added ACGT to %s and got %s " % (dna, longer_dna)) exit()
例子6-1输出如下:
I added ACGT to CGACGTCTTCTCAGGCGA and got CGACGTCTTCTCAGGCGAACGT
我们现在来看下这段代码,首先注意的是程序有两个部分。第一部分是函数addACGT定义,第二部分是程序主体执行。在Python中,程序是顺序执行,若函数addACGT定义在函数调用步骤后面,程序会出错。如你所见,例子6-1非常简单。它首先将一些DNA存储在变量dna中,然后该变量作为参数传递给函数调用,接着函数返回的值保存在变量longer_dna中,最后打印,程序退出。
接下来,我们将了解函数定义以及它如何使用作用域的原理。
2.函数和作用域
函数由保留字def定义,后接函数名称和圆括号();任何传入参数和自变量必须放在圆括号中,圆括号支架可以用于定义参数;函数内容以冒号起始,并且缩进;return语句结束函数,返回值或None。
在例子6-1中,函数名称是addACGT,块是缩进内容,这是函数定义:
def addACGT(dna): dna += ‘ACGT‘ return dna
现在我们看看函数中的内容。
函数就像是主程序的独立辅助程序,它需要由自己的变量。你将在本专辑的函数中使用两种类型的变量:
-
- 传入函数的参数
- 函数内定义的参数,并且限制在函数范围内使用
参数是指函数使用或调用时给予的值,参数的值通过函数名称后面的括号进行传递。最后,大多数函数通过return语句返回函数结果,在例子6-1中,函数addACGT,返回了字符串。
2.1 参数
当你使用某些参数调用函数时,你在调用中使用的参数名称在函数中并不重要,只有在函数中实际传递的参数值才是最重要的。
2.2 作用域
在Python中,使用一个变量时并不严格要求需要预先声明它,但是在真正使用它之前,它必须被绑定到某个内存对象(被定义、赋值);这种变量名的绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量。
3.命令行参数和列表
例子6-2是另一个使用函数的程序,你可以使用命令行提供程序所需的信息(例如,文件路径或者DNA字符串),而不用交互式的输出。例子6-2还显示了如何使用列表,通过下标来访问列表中特定元素。
对于命令行程序,键入程序名称,然后键入程序的参数(如果有),然后按回车键启动程序。在例子6-2中,当用户键入程序名名称时,接着键入程序计算所需的DNA字符串,程序调用户返回如下结果:
AAGGGGTTTCCC The DNA AAGGGGTTTCCC has 4 G‘s in it!
例子6-2 在命令行中计算DNA中G的个数
#!/usr/bin/env python # Counting the number of G‘s in some DNA on the command line import sys ################################################################################ # Subroutines for Example 6-3 ################################################################################ def countG(dna) # return a count of the number of G‘s in the argument dna # Use the fourth method of counting nucleotides in DNA, as shown in # Chapter Four, "Motifs and Loops" count = dna.count(‘G‘) + dna.count(‘g‘) return count } # Collect the DNA from the arguments on the command line # when the user calls the program. # If no arguments are given, print a USAGE statement and exit. # $0 is a special variable that has the name of the program. USAGE = "%s DNA " % sys.argv[0] # sys.argv is an array containing all command-line arguments. # # If len(sys.argv) < 2 , the test will fail and the print USAGE and exit # statements will be called. if len(sys.argv) < 2: print(USAGE) exit() # Read in the DNA from the argument on the command line. dna = sys.argv[1] # Call the subroutine that does the real work, and collect the result. num_of_Gs = countG(dna) # Report the result and exit. print(" The DNA $dna has %s G‘s in it! " % num_of_Gs) exit()
现在让我们看看这个程序是如何工作的,同时查看些新的用法。Python中内置模块sys提供了程序与python解释器交互的函数和变量,因此你可以方便的使用命令行参数。sys.argv列表中0索引位置是从命令行调用的程序名称。在例子6-2中,我们使用了if语句来判断参数是否为空(len(sys.argv)<2),如果为空,则打印USAGE退出。
另外一点是,例子6-2使用索引来提取元素。例如,提取第一个参数,sys.argv列表中索引为0的元素:
sys.argv[0]
4.模块和库中的函数
当你开始编写程序时,你会发现从你现有程序中复制它们并将它们粘贴到新程序中,然后这样的代码在多个程序中出现,使得代码有点冗余,有时需要修改函数时,你必须得修改所有的副本。有一种方便方法是将函数收集到称为模块或库的文件中,然后使用Python中内置函数import,导入库或模块中的函数。
我们将定义一个BeginPythonBioinfo.py,然后你可以将所有函数定义在其中。现在,如果要使用BeginPythonBioinfo.py中任何函数,你只需要将以下语句放在代码开头:
import BeginPythonBioinfo
注意,不需要后缀.py,这是Python处理模块名称的方式。关于使用模块将在函数还有最后一件事要知道:Python程序需要知道在哪里找到模块,如果是在一个文件夹下完成所有工作,那么一切正常。(如果不是,超出本专辑范围了,请阅读Python相关文档)。
5.修复代码中错误
现在让我们来看看当程序出错时该怎么办。
程序可能会以多种方式出错,也许根本不会运行。查看错误消息,尤其是第一行或第二行错误消息,会找出问题,例如,缩进错误,少了括号等等。
另外,程序能运行,但结果不符合,那么程序的逻辑有些问题。python有几种方法可以帮助你查找和修复程序中的错误。
5.1 使用注释和print语句修复错误
有时你可以通过选择性注释掉程序的各个部分来找到导致问题出现的部分;你还可以添加print语句来检查某些变量正在执行的操作,这些都是历史悠久的编程技巧,几乎适用于任何编程语言。
5.2 使用python调试工具
代码中的错误问题是一旦程序开始运行,你只能看到输出,不能看到程序正在执行的步骤。使用Python调试工具处理程序中错误,可以逐步详细检查程序,能够快速解决问题。
某些情况下,Python调试器无法很好地处理:例如:依赖时序考虑的交互过程。调试器一次只能检查一个程序,并且在检查时,它会停止程序,因此会产生与其他进程的时序交互问题。
本节将介绍Python内置调试模块pdb,及其重要的功能。
#!/usr/bin/env python
import pdb def printbase(dna): pdb.set_trace() for b in dna: print(b) dna = ‘ATAGtcagatgataagtaga‘ printbase(dna)
上述代码中,pdb.set_trace()为程序设置断点,断点告诉调试器停止执行,以便你可以在代码中查找错误。Python调试器允许你以格之格方式设置断点,程序运行仅在到达带有断点的语句时停止检查它,这样你就不必逐步执行每行代码。另外,Python有许多开发工具自带调试工具(例如,Pycharm、Eclipse等等)。
6.练习
6.1 编写一个函数来连接两个DNA字符串
6.2 编写一个函数返回DNA字符串中每个核苷酸的百分比。
6.3 编写函数提示用户输入任何内容,并返回用户收集的答案。
6.4 编写函数查找命令行参数,例如,-help、-h和--help。
6.5 编写函数检查文件是否存在,是否为常规文件,且大小为非零。
6.6 编写一个包含函数的模块,该函数返回有关DNA序列的各种统计数据,例如长度、GC含量,polyT序列的存在与否,或其它信息。
6.7 阅读有关调试器的文档,并通过编程来熟悉它的使用。
以上是关于第六章 函数和调试的主要内容,如果未能解决你的问题,请参考以下文章