返回介绍

第 4 章 函数

发布于 2025-02-19 23:42:45 字数 11147 浏览 0 评论 0 收藏 0

4.1 函数调用

编程中的函数是执行计算的一个命名语句序列。当定义函数时,需要指定函数名和语句顺序。定义好之后,通过函数名就可以调用函数。之前我们见到过一个函数示例:

>>> type(32)
<type 'int'>

这个函数的名称是 type 。括号里面的表达式称为函数的参数。参数可以是一个值或变量,作为函数的输入。type 函数的作用是显示参数的类型。

一般而言,函数获取参数,然后返回结果。这个结果称为返回值。

4.2 内置函数

Python 提供了很多重要的内置函数,不需要我们预先定义就可以使用。Python 开发者定义了解决常见问题的函数集,内置在 Python 中,供我们使用。

max 函数和 min 函数分别求得一个列表中的最大值和最小值:

>>> max('Hello world')
'w'
>>> min('Hello world')
' '
>>>

max 函数告诉我们,这个字符串中最大的字符是(“w”),min 函数告诉我们这个字符串中最小的字符是空格符。

另一个常用的内置函数是 len 函数,它能返回参数的长度。如果 len 函数的参数是一个字符串,那么它返回的是这个字符串的字符数。

>>> len('Hello world')
11
>>>

这些函数不仅可用于字符串,还能操作其他数据类型,后续章节会涉及到。

你可以将内置函数类比为保留字,举例来说,不要使用“max”作为变量名。

4.3 类型转换函数

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'

4.4 随机数

既定输入的情况下,大部分的计算机程序每次都会产生相同的输出,也就是说这是板上钉钉的事情。一般而言,确定性是好的,我们希望同一个算法产生相同的结果。然而,有些时候我们希望计算机具备不确定性,比如游戏就是一个典型例子,当然还有很多其他例子。

让一个程序完全处于不确定状态,这很难做到。但是,有一些方法可以让它看起来具有不确定性。一种方法是利用算法产生伪随机数。伪随机数并不是真正的随机数,原因在于它们是利用确定的算法计算出来的。单看这些数字并不会发现它们和真正的随机数有什么不同。

随机数模块提供了伪随机数生成的函数,以下简称“random”。

random 函数返回一个介于 0.0-1.0 的随机浮点数(包括 0.0,但不包括 1.0)。每次调用时,你会得到一长串的数字。举例来说,执行下面的循环语句:

import random

for i in range(10):
  x = random.random()
  print x

程序生成了 10 个介于 0.0-1.0 之间但不包括 1.0 的随机数。

0.301927091705
0.513787075867
0.319470430881
0.285145917252
0.839069045123
0.322027080731
0.550722110248
0.366591677812
0.396981483964
0.838116437404

习题 4.1 运行以下程序并观察得到的结果,不妨多运行几次看看。

random 函数只是众多随机数函数中的一个。randint 函数传入两个整数参数,并返回介于这两个参数之间的整数(包括这两个参数在内)。

>>> random.randint(5, 10)
5
>>> random.randint(5, 10)
9

为取得一组值中的随机元素,可以使用下面的程序:

>>> t = [1, 2, 3]
>>> random.choice(t)
2
>>> random.choice(t)
3

random 模块还提供了从连续分布函数中提取随机值的函数,包括高斯函数、指数函数、伽玛函数等。

4.5 数学函数

Python 的 math 模块提供了很多常见的数学函数。在使用之前要导入相应的包:

>>> import math

这条语句创建了一个名为 math 的模块对象。如果将这个模块对象打印出来,就会得到关于它的一些信息:

>>> print math
<module 'math' from '/usr/lib/python2.5/lib-dynload/math.so'>

这个模块对象包括了模块中定义的函数和变量。为了使用其中的函数,你必须指定模块名和函数名,用圆点隔开,也就是英文的句号。这种格式叫做 点标记 (dot notation)。

>>> ratio = signal_power / noise_power
>>> decibels = 10 * math.log10(ratio)

>>> radians = 0.7
>>> height = math.sin(radians)

第一个例子是计算以 10 为底的信噪比的对数。math 模块还提供了 log 函数用来求以 e 为底的对数。

第二个例子调用了正弦函数。变量名字给出了提示,除了正弦函数,还有余弦,正切等其他三函数都采用弧度作为参数。将度数转换成弧度的运算是除以 360,然后乘以 2π:

>>> degrees = 45
>>> radians = degrees / 360.0 * 2 * math.pi
>>> math.sin(radians)
0.707106781187

math.pi 表达式从 math 模块中取得变量 pi 的值。这个变量是π的一个近似值,保留了小数点后 15 位数。

运用你的平面几何知识,与 2 的平方根再除以 2 的结果进行比较,检查程序输出是否正确。

>>> math.sqrt(2) / 2.0
0.707106781187

4.6 定义新函数

目前为止,我们只是使用了 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 个空格。函数体可以包括任意数量的语句。

print 语句要输出的字符串放在双引号里面。单引号和双引号作用是一样的。除了在字符串中也有单引号的冲突情况之外,大多数人会使用单引号。

如果你在 Python 交互模式中定义函数,解释器会打印省略号(…)表明函数定义还没有结束。

>>> 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>
>>> print 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.

函数定义好之后,就可以在其他函数中使用。例如,为了重复调用之前的函数,你可以写一个 repeat_lyrics 函数:

def repeat_lyrics():
  print_lyrics()
  print_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.

现实中歌曲可不是这样填词的。

4.7 定义与用法

将前面的代码片段组合在一起,完整的程序如下:

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。函数定义会像其他语句一样执行,但它的作用仅仅是新建函数。函数体里面的代码并没有被执行,直到被调用时才会执行,函数定义是没有输出的。

正如所期望的,你必须在执行函数之前定义这个函数。换句话说,函数在第一次被调用之前就应该定义好。

习题 4.2 将上面程序的最后一行调到最开始,这样函数调用就出现在函数定义之前。运行程序并观察出错信息。

习题 4.3 将函数调回底部,将函数 print_lyrics 定义放在函数 repeat_lyrics 后面,这样会产生什么结果?

4.8 执行流程

为了保证函数定义是在第一次使用之前,你就必须知道语句的执行顺序,这称为执行流程。

程序一般从第一条语句开始执行,从上到下依次顺序执行。

函数定义并不改变程序的执行顺序,但要记住,函数只有被调用的时才会被执行。

函数调用像是是执行流程中的一个迂回。当遇到要调用的函数时,不会接着去执行下一句,而是跳到函数体,执行完函数体里的语句,然后回到刚才跳出的地方继续执行。

当你掌握了函数之间的调用,这就会变得很简单。在一个函数中,程序可能需要执行另一个函数的语句。还有,在定义新函数时,程序还可能需要执行另一个函数!

程序里跳来跳去,这说明了什么?阅读程序有时候没必要从头看到尾。有时候按照执行流程会让你更好地读懂程序。

4.9 形式参数与实际参数

我们之前见到的内置函数需要传入参数。例如,调用 math.sin 函数需要传入一个数值作为参数。有些函数不止有一个参数:math.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 本身。

4.10 带返回值的函数和空函数

我们使用过的有些函数(如 math 函数)有返回值。由于没有更好的叫法,我称之为有返回值的函数。其他的一些函数比如 print_twice,执行任务但不返回值,这种称为空函数。

当调用有返回值的函数时,大部分时候你想要利用返回值,例如,将它赋值给一个变量,也可以作为表达式的一部分:

x = math.cos(radians)
golden = (math.sqrt(5) + 1) / 2

在交互模式中调用函数时,Python 会显示结果。

>>> math.sqrt(5)
2.2360679774997898

在程序文件中,如果不把有返回值的函数的返回值保存在变量里的话,返回值就会丢失。

math.sqrt(5)

空函数也许会在屏幕上输出什么或者有其它的用途,但其本身没有返回值。如果你试着将结果赋值给一个变量,就会得到一个特殊的值 None。

>>> result = print_twice('Bing')
Bing
Bing
>>> print result
None

None 值与字符串’None’是不一样的。它是一种特殊的值并且拥有自己的类型。

>>> print type(None)
<type 'NoneType'>

使用 return 语句从函数中返回结果。例如,创建一个简单的函数 addtwo,将两个数相加并且返回一个值。

def addtwo(a, b):
  added = a + b
  return added

x = addtwo(3, 5)
print x

在文件中执行程序时,print 语句会打印出 8。addtwo 函数被调用时,传入 3 和 5 这两个参数。带有形参 a 和 b 的函数,分别是 3 和 5。这个函数将两个数相加,然后将结果赋值给局部变量 added,最后使用 return 语句将计算结果赋值给 x 并打印出来。

4.11 为什么需要函数?

如果不太明白为什么需要花费精力把程序分成多个函数,请看如下理由:

  • 定义新函数可以让你有机会为一组语句命名,使得程序易于阅读、理解和调试。
  • 函数可以消除冗余代码,为程序瘦身。如果想修改函数,只需要改变一处即可。
  • 将长程序分割成多个函数,逐一调试,最后再整合在一起。
  • 精心设计的函数对很多程序而言都会有用。一旦创建并调试后,你就可以重复使用它。

本书其余部分会使用函数定义来解释一个概念。创建并正确使用函数的技巧之一是捕捉到一个想法,例如,“找出列表中的最小值”。随后我们会介绍 min 函数的代码,它将列表的值作为参数输入,然后返回其中的最小值。

4.12 调试

制表符和空格通常不可见,导致不易调试,因此最好选择一个能够自动缩进的文本编辑器。

另外,运行程序之前不要忘记保存。有一些开发环境可以自动保存,而有一些不行。未保存的情况下运行的程序和文本编辑器的程序不一样。

如果你重复运行相同错误的程序,那么会调试很久。

确保运行的代码是文本编辑器中你所看到的代码。如果不确定,你可以在程序开头加入 print 'hello' 这样的代码再运行。如果没有看到 hello 输出,那么就没有正确运行程序。

4.13 术语

算法: 解决一类问题的一般过程。

实际参数: 函数被调用时传入的值。函数中这个值会被赋予相应的参数。

函数体: 函数定义中的一组语句序列。

组合: 一个表达式作为另一个表达式的一部分,或者一条语句作为另一条语句的一部分。

确定性: 在既定输入下,程序每次运行都一样并返回相同的结果。

点标记: 在另一个模块中调用函数时,需要指定模块名和函数名,用句号隔开。

执行流程: 程序运行时的语句执行顺序。

带返回值的函数: 能够返回一个值的函数。

函数: 执行一些有用操作的命名语句序列。函数不一定有参数,也不一定返回结果。

函数调用: 执行函数的语句,包括函数名和参数列表。

函数定义: 创建新函数的语句,需要指定名称、参数和执行语句。

函数对象: 通过函数定义创建的值。函数名就是参引函数对象的变量。

函数头部: 函数定义的第一行。

import 语句: 读取模块文件并创建模块对象的语句。

模块对象: import 语句创建的一个值,用以提供模块中定义的数据和代码。

形式参数: 函数中用来参引传入参数的变量名。

伪随机数: 看起来像随机数的一串数值,不过这是由确定的程序产生的。

返回值: 函数的结果。如果一个函数调用作为表达式来使用,那么返回值就是这个表达式的值。

空函数: 没有返回值的函数。

4.14 练习

习题 4.4 Python 中”def”保留关键字的作用是什么?

a) 它是一个俚语,“下面的代码太酷了”

b) 它表示函数的开始

c) 它表示接下来代码的缩进部分会被存储

d) b 和 c 都正确

e) 以上都不正确

习题 4.5 下面的 Python 语句会打印出什么?

def fred():
   print "Zap"

def jane():
   print "ABC"

jane()
fred()
jane()

a) Zap ABC jane fred jane

b) Zap ABC Zap

c) ABC Zap jane

d) ABC Zap ABC

e) Zap Zap Zap

习题 4.6 重写之前的工资计算程序,加班时间的工资按原工资的 150%计算。创建一个 computepay 函数,包含两个参数 hours 和 rate。

Enter Hours: 45

Enter Rate: 10

Pay: 475.0

习题 4.7 重写之前的分数计算程序,使用 computegrade 函数,score 作为参数,返回一个评分等级的字符串。

Score   Grade
> 0.9   A
> 0.8   B
> 0.7   C
> 0.6   D
<= 0.6  F


Program Execution:

Enter score: 0.95
A

Enter score: perfect
Bad score

Enter score: 10.0
Bad score

Enter score: 0.75
C

Enter score: 0.5
F

重复运行该程序,每次输入不同的值来测试输出结果。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文