本文翻自:Allen B. Downey ——《Think Python》 原文连接:http://www.greenteapress.com/thinkpython/html/thinkpython004.html 翻译:Simba Gu
[自述:感谢coneypo网友的建议,译文的排版和书写规范已经稍做调整,但愿看起来能比前面翻译的几篇舒服一些 :)]
在程序中,函数能够理解为一个有名字的执行命令的序列。当你在定义一个函数的时候,必须为其指定一个名字,而后再经过这个名字来调用。正如前一章里面提到过的函数调用的例子:html
>>> type(32) <type 'int'>
这里的type就是一个函数,括号里面的表达式叫作参数,它返回传入参数的类型。一般来讲就是函数“接收”值并“返回”结果,这个结果叫作返回值。python
Python内置了从一种类型转换为其余类型的函数。例如 int 函数能够把任何可以转换成整数的值转换成整数。编程
>>> int('32') 32 >>> int('Hello') ValueError: invalid literal for int(): Hello
int 函数能够把浮点数转换成整数,它会直接把小数部分剔除而非对转换的浮点数进行四舍五入。数组
>>> int(3.99999) 3 >>> int(-2.3) -2
float 函数能够把整数或字符串转换成浮点数:框架
>>> float(32) 32.0 >>> float('3.14159') 3.14159
最后,str 函数能够把参数转换成字符串编程语言
>>> str(32) '32' >>> str(3.14159) '3.14159'
Python提供了一个包含大多数经常使用的数学函数的模块,这个模块是一个包含数学相关函数的集合的文件,在使用里面函数以前,须要用import命令导入一下:编辑器
>>> import math
这条语句表示创建了一个名为math的模块对象,你能够经过print函数来显示这个模块的相关信息:函数
>>> print math <module 'math' (built-in)>
次模块对象中包含了这个模块里定义的全部函数和变量。访问里面包含的函数须要指定模块名称和函数名称,中间用“.”隔开,这种格式称之为“点语法”。学习
>>> ratio = signal_power / noise_power >>> decibels = 10 * math.log10(ratio) >>> radians = 0.7 >>> height = math.sin(radians)
第一个例子是调用log10函数以分贝为单位来计算信噪比(假设变量 signal_power 和 noise_power已经定义),math模块也提供了以log函数计算以e为底的对数。测试
第二个例子是求radians的正弦值,从参数的名字咱们还能够联想到除了例子里面出现的 sin 函数之外,还有其它的相关的三角函数,如 cos,tan 等等,这些函数都有一个弧度参数。
角度转换为弧度的公式为:角度 / 360 * 2 π:
>>> degrees = 45 >>> radians = degrees / 360.0 * 2 * math.pi >>> math.sin(radians) 0.707106781187
上面表达式中的 math.pi 表示从math 模块中获取pi的值,它的值等于圆周率π的近似值,大概精确到15位小数。若是你想更多的了解三角函数,你能够拿前面例子的计算结果跟平方根除以2来验证一下。
>>> math.sqrt(2) / 2.0 0.707106781187
到目前为止,咱们已经初步了解了程序的基本元素:变量、表达式和语句,记下来咱们将讨论如何把它们组合起来。编程语言最实用的特性就是构建功能块并组合起来实现特定功能。函数的参数能够是任何表达式,甚至能够是算术运算符,例如:
x = math.sin(degrees / 360.0 * 2 * math.pi)
函数甚至还能够这样调用:
x = math.exp(math.log(x+1))
有一个须要注意的地方就是:变量名必须放在赋值语句的左边,赋值语句右边能够是任何表达式。若是表达式放在右边将会触发语法错误(后面咱们将会了解到该异常的规则)。
>>> minutes = hours * 60 # right >>> hours * 60 = minutes # wrong! SyntaxError: can't assign to operator
目前,咱们只学习了如何使用Python内置的函数,固然咱们也能够添加新的函数。函数定义须要指定一个函数名称和函数调用执行的语句,例如:
def print_lyrics(): print "I'm a lumberjack, and I'm okay." print "I sleep all night and I work all day."
def 是表示定义一个函数的关键字,函数名称为 print_lyrics。函数名的命名规则跟变量名的命名规则是同样的:由字母、数字和一些合法的字符,固然首字符不能是数字,函数名不能跟关键字重名,另外还须要避免函数名跟变量名同名。函数名称后面若是是一对空的括号则表示这个函数没有参数。
函数定义的第一行叫作头,其他部分叫作主体,函数头必须以“:”结尾,而且函数体必须总体缩进。按惯例,缩进一般是4个空格(或一个tab缩进符),除此之外,函数体能够包含任意数量的语句。
print语句中的字符串能够用一对单引号或者一对双引号括起来,大部分人习惯用单引号,可是若是引用的字符串包含单引号(撇号),那就建议使用双引号,反之亦然。若是你在交互模式下面定义函数,解释器会打印省略号(...)来提醒你函数定义还没有结束:
>>> def print_lyrics(): ... print "I'm a lumberjack, and I'm okay." ... print "I sleep all night and I work all day." ...
在交互模式下,结束函数的定义必须输入一个空行来告诉解释器(在脚本模式则无必要),另外结束函数定义以后,系统会自动创建一个同名变量。
>>> print print_lyrics <function print_lyrics at 0xb7e99e9c> >>> type(print_lyrics) <type 'function'>
因而可知 print_lyrics 是一个函数类型的函数对象,调用自定义函数的语法与调用内置函数的方法是同样的:
>>> print_lyrics() I'm a lumberjack, and I'm okay. I sleep all night and I work all day.
当你定义好了一个函数,你也能够在其余函数里面调用它。若是咱们要重复打印print_lyrics函数的内容,咱们能够写一个新函数 repeat_lyrics 来实现这个功能:
def repeat_lyrics(): print_lyrics() print_lyrics()
当调用函数 repeat_lyrics 的时候你将会看到下面的结果:
>>> repeat_lyrics() I'm a lumberjack, and I'm okay. I sleep all night and I work all day. I'm a lumberjack, and I'm okay. I sleep all night and I work all day.
固然,这首歌实际上不是这么唱的。
将前面一节里面的代码放在一块儿以下所示:
def print_lyrics(): print "I'm a lumberjack, and I'm okay." print "I sleep all night and I work all day." def repeat_lyrics(): print_lyrics() print_lyrics() repeat_lyrics()
这段程序包含了两个函数的定义:print_lyrics 和 repeat_lyrics。函数定义的语句也会被执行,但仅限于创建函数对象并不产生输出内容,只有当函数被调用的时候,函数体里面定义的语句才会被执行并输出数据。
所以正如你所想的那样,在调用函数以前必须先进行定义,也就是说,在你调用函数以前,函数定义部分必须先被执行。
将程序的最后一行放到第一行,而后运行程序看看会获得什么出错信息。
将函数调用的语句放回程序最后,而后把 print_lyrics 函数的定义放到 repeat_lyrics 函数的后面,而后运行程序看看会获得什么错误信息。
为了确保程序调用以前被定义,咱们必须清楚语句执行的顺序,这个就叫作执行流程。程序执行老是从第一行语句开始依次执行,而且一次执行一行。函数的定义是不会改变程序的执行流程的,请牢记函数里面的语句只有在函数被调用的时候才会被执行。函数调用就像执行流程里面的一条弯路,它会跳转并执行函数里定义的语句,结束以后再回到函数调用的地方接着执行下面的语句。
你只要记住在一个函数里面是能够调用其它函数的,这样会更容易理解。当一个函数里面调用另外一个函数的时候,程序必须执行另外一个函数里面的语句,而且在完成以后再继续执行这个函数的其它语句。
幸运的是Python很擅长跟踪,程序在调用函数的时候都能准确的知道调用的位置。当程序终止的时候,它会跳转到程序的最后。
这样作有什么寓意吗?事实上你并不是每次都是从头开始阅读程序代码,只是有时候从头开始阅读代码并遵循执行流程会让你以为有意义。
咱们发现一些内置函数是有争议的。例如,当你调用math.sin函数时须要传递一个实参,可是像 matn.pow 函数则须要传入两个实参:基数和指数。
在函数内部,实参是一个被分配给了形参的变量,这里有一个须要传入一个参数的用户自定义函数:
def print_twice(bruce): print bruce print bruce
这个函数把实参分配给了一个形参变量 bruce,当这个函数被调用的时候,它会打印两次形参的内容(无论传过来实参是什么内容),而且能够打印任何能够打印的值。
>>> print_twice('Spam') Spam Spam >>> print_twice(17) 17 17 >>> print_twice(math.pi) 3.14159265359 3.14159265359
固然,适用于内置函数的规则一样也适用于用户自定义函数,咱们能够传递任意表达式做为参数调用 print_twice 函数:
>>> print_twice('Spam '*4) Spam Spam Spam Spam Spam Spam Spam Spam >>> print_twice(math.cos(math.pi)) -1.0 -1.0
参数是在函数被调用以前就进行运算的,所以表达式 'Spam '*4 和 math.cos(math.pi) 是会被运算一下再传递给函数。
另外,你也能够用一个变量做为参数传递给函数:
>>> michael = 'Eric, the half a bee.' >>> print_twice(michael) Eric, the half a bee. Eric, the half a bee.
咱们把变量名 michael 做为实参传递给函数,这个跟函数的形参 bruce 是无关的,在函数内部(调用者)这个值叫什么并不重要,在函数 print_twice 里,它把全部传递过来的参数都叫 bruce。
当你在函数里面定义一个变量的时候,它只能在函数内部使用,所谓局部变量,例如:
def cat_twice(part1, part2): cat = part1 + part2 print_twice(cat)
这个函数有两个参数,实现链接这两个参数并打印两次的功能,调用方式以下:
>>> line1 = 'Bing tiddle ' >>> line2 = 'tiddle bang.' >>> cat_twice(line1, line2) Bing tiddle tiddle bang. Bing tiddle tiddle bang.
当 cat_twice 函数调用结束,变量 cat 是会被销毁的,若是此时你尝试打印该变量的值,咱们就会获得一个异常:
>>> print cat NameError: name 'cat' is not defined
事实上,函数的参数也是局部的,例如,在 print_twice 以外是没有哪一个叫 bruce 的变量。
为了跟踪变量在哪里被使用了,所以有时候画一个堆栈图会颇有帮助。与状态图相似,堆栈图能够显示每个变量的值,可是它还能够显示出每个变量属于哪个函数。
每一个函数由一个方框来表示,每一个方框边上写下函数的名字,里面写下函数的参数和变量。前面例子的堆栈图如图3.1所示。
图3.1:堆栈图
全部的方框按序列在一个堆栈里,而且标注其调用的对应的函数。在这个例子中,print_twice 被 cat_twice 调用,而cat_twice被__main__调用,__main__是最顶层的方框的特殊名称,你在函数以外定义的全部变量都属于__main__。
每一个参数都引用了其对应实参的值,所以,part1和line1的值相同,part2和line2的值相同,bruce的值则与cat相同。
若是在调用函数的时候出错了,Python将会打印所调用的函数名称,以及调用该函数的函数名称,并最终回到主函数 __main__。
例如,若是你在 print_twice 函数访问cat变量就会获得一个NameError:
Traceback (innermost last): File "test.py", line 13, in __main__ cat_twice(line1, line2) File "test.py", line 5, in cat_twice print_twice(cat) File "test.py", line 9, in print_twice print cat NameError: name 'cat' is not defined
这个列表中的函数称为回溯,它能告诉你哪一个程序文件出错,出错的行,以及正在执行哪些函数,而且能告诉你哪一行代码致使了错误。在回溯列表中的函数顺序跟堆栈图中的函数顺序是同样的,当前运行的函数在最下方。
有一些咱们使用的函数是有返回值的,例如数学函数。因为没有更好的名字,我暂且称之为有返回结果函数,像print_twice只执行一些指令的函数称之为无返回结果函数。
当你调用一个有返回值的函数,确定是但愿能利用返回结果作些什么,例如,把返回结果赋值给一个变量或者拿它做为表达式的一部分:
x = math.cos(radians)
golden = (math.sqrt(5) + 1) / 2
当你在交互模式下调用函数,Python会把结果显示出来。
>>> math.sqrt(5)
2.2360679774997898
可是在脚本模式下,若是你调用一个有返回值函数,结果是不会显示出来的。
math.sqrt(5)
这个脚本是计算5的平方根,可是并无保存或显示计算结果,所以不是颇有用。
无返回值函数可能会显示一些内容或者产生其它影响,可是没有返回结果,若是你尝试把函数结果赋值给一个变量,将会获得一个特殊类型的返回值None。
>>> result = print_twice('Bing') Bing Bing >>> print result None
这个None值跟字符串‘None’是不同的,它是一个特定类型的特殊值:
>>> print type(None) <type 'NoneType'>
到目前为止,咱们写的函数都是无返回值的函数,接下来的几章咱们将开始写一些有返回值的函数。
或许你还不明白为何要把一个程序拆分红函数,这里有几条理由:
Python提供了两种方式导入模块,咱们已经用过其中一种:
>>> import math >>> print math <module 'math' (built-in)> >>> print math.pi 3.14159265359
若是导入了math模块,你就会获得一个名为math的模块对象,而且这个模块对象里面已经包含了变量pi和一些数学函数,例如 sin 和 exp 等等,可是你不能这样直接获取 pi 的值:
>>> print pi Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'pi' is not defined
另外还有一种import方式,你能够直接导入模块里面的一个对象:
>>> from math import pi
这样你就能够直接获取pi值而不须要点操做。
>>> print pi 3.14159265359
或者你可使用星号操做符导入模块内的全部对象:
>>> from math import * >>> cos(pi) -1.0
把math模块全部对象导入进来的好处是可让代码看起来更简洁,可是很差的地方是在不一样模块之间,或者导入的模块里面的变量名与你本身模块里面的变量名可能会有冲突。
若是你使用文本编辑器写脚本,可能会遇到空格或者制表符的问题。避免这个问题的最好方法就是都使用空格(不要使用制表符)。大部分Python编辑器默认状况下都是如此设置的,只有部分编辑器不是这样。由于制表符和空格是不可见的,所以很难去进行调试,因此尽可能找一个合适的能管理代码缩进的编辑器。
另外,运行程序以前记得先保存代码,由于有些编辑器会自动保存,而有些编辑器则不会。所以你须要确认运行的程序代码是否跟你编辑的代码是否是同样的,调试的时候若是你没有注意到这一点,而是一遍又一遍的运行没有保存的代码,这将会浪费你不少时间。
因此,请确保你正在运行的代码就是你正在查看的代码,若是你不肯定,就在代码里加一些代码,如 print "Hello" 放在代码最前面再运行,若是没有输出 Hello,那就证实了你运行了不正确的代码。
Python提供了一个能够获取字符串长度的内置函数 len,例如 len('allen')的值为5.
请编写一个函数right_justify ,参数名为 s,实现功能为:打印一个长度为70字符串。
>>> right_justify('allen') allen
函数对象是能够赋值给一个变量或者做为一个参数传递的。例如:do_twice 是一个函数,其又一个函数对象参数,而且调用该函数两次:
def do_twice(f): f() f()
这里又一个例子:利用 do_twice 调用 print_spam 函数两次。
def print_spam(): print 'spam' do_twice(print_spam)
把代码放入脚本并测试。
答案请参考:http://thinkpython.com/code/do_four.py
这个练习只能使用咱们已经学过的语句来实现。
写一个函数画出以下网格:
+ - - - - + - - - - +
| | |
| | |
| | |
| | |
+ - - - - + - - - - +
| | |
| | |
| | |
| | |
+ - - - - + - - - - +
提示:打印多个字符串能够用逗号隔开: print '+', '-'
若是打印序列以逗号结尾,则表示该行的打印内容还没有结束,接下来的打印内容会在同一行。
print '+',
print '-'
这条语句的输出结果是 '+ -'.
每个打印语句结束会另起一行,写一个函数画一个4行4列的网格。
参考答案:http://thinkpython.com/code/grid.py
#英文版权 Allen B. Downey #翻译中文版权 Simba Gu #转载请注明出处