Automate the Boring Stuff with Python: Practical Programming for Total Beginners (2nd Edition)
Written by Al Sweigart.
The second edition is available on 2019.10.29
函数的一个主要目的就是将需要多次执行的代码放在一起。
一般来说,我们总是希望避免复制代码,因为如果一旦决定要更新代码(比如说,发现了一个缺陷要修复),就必须记住要修改所有复制的代码。
随着编程经验的增加,常常会发现自己在消除重复(deduplicating)代码,即去除一些重复或复制的代码。消除重复能够使程序更短、更易读、更容易更新。
3.1 带参数的 def 语句
def hello(name):
print('Hello ' + name)
hello('Alice')
3.2 返回值和 return 语句
import random
def getAnswer(answerNumber):
if answerNumber == 1:
return 'It is certain'
elif answerNumber == 2:
return 'It is decidedly so'
elif answerNumber == 3:
return 'Yes'
elif answerNumber == 4:
return 'Reply hazy try again'
elif answerNumber == 5:
return 'Ask again later'
elif answerNumber == 6:
return 'Concentrate and ask again'
elif answerNumber == 7:
return 'My reply is no'
elif answerNumber == 8:
return 'Outlook not so good'
elif answerNumber == 9:
return 'Very doubtful'
# random.randint(1, 9)求值为 1到 9之间的随机整数(包括 1和 9)
fortune = getAnswer(random.randint(1, 9))
print(fortune)
3.3 None 值
在 Python 中有一个名为 None
的值,它表示没有值。None
是 NoneType
数据类型的惟一值。
在后台,Python 在任何没有 return
语句的函数定义的末尾添加 return None
。
此外,如果使用没有值的 return
语句 (即仅返回关键字本身),则返回 None
值。
3.4 关键字参数和 print()
关键字参数 (keyword arguments) 由函数调用中加在它们前面的关键字来识别的。关键字参数通常用于可选参数。
例如,print()
函数有两个可选参数 end
和 sep
,
end
指定应该在其参数末尾打印什么;
sep
指定参数之间打印什么来将它们隔开。
print('Hello', end='')
print('World')
print('cats', 'dogs', 'mice')
print('cats', 'dogs', 'mice', sep=',')
上面代码运行结果为:
HelloWorld
cats dogs mice
cats,dogs,mice
3.5 局部和全局作用域
一个变量要么是全局变量,要么是局部变量,不能既是局部的又是全局的。
如果不同的变量位于不同的作用域中,则可以对它们使用相同的名称。也就是说,可以有一个名为 spam 的局部变量和一个名为 spam 的全局变量。
def spam():
eggs = 'spam local'
print(eggs) # prints 'spam local'
def bacon():
eggs = 'bacon local'
print(eggs) # prints 'bacon local'
spam()
print(eggs) # prints 'bacon local'
eggs = 'global'
bacon()
print(eggs) # prints 'global'
上面代码运行结果如下:
bacon local
spam local
bacon local
global
3.6 global 语句
如果想要在函数中修改存储在全局变量中的值,则必须对该变量使用 global 语句。
def spam():
global eggs
eggs = 'spam' # this is the global
def bacon():
eggs = 'bacon' # this is a local
def ham():
print(eggs) # this is the global
eggs = 42 # this is the global
print(eggs) # 打印 42
spam()
print(eggs) # 打印 ‘spam’
在一个函数中,如果试图在局部变量赋值之前就使用它,像下面的程序这样,Python 就会报错。
def spam():
print(eggs) # ERROR!
eggs = 'spam local'
eggs = 'global'
spam()
上面代码运行后报错信息为:
UnboundLocalError: local variable 'eggs' referenced before assignment
发生这个错误是因为,Python 看到 spam() 函数中有针对 eggs 的赋值语句,因此认为 eggs 变量是局部变量。但是因为 print(eggs) 的执行在eggs 赋值之前,局部变量 eggs 并不存在。
函数作为“黑盒”
通常,需要了解的关于函数的所有信息就是它的输入 (参数) 和输出值,不必总是为函数代码的实际工作方式而烦恼。当以这种高级方式思考函数时,通常会说将函数视为“黑盒”。
这个思想是现代编程的基础。不需要了解这些函数是如何工作的,就可以使用它们。
3.7 异常处理
def spam(divideBy):
return 42 / divideBy
print(spam(12))
print(spam(0))
print(spam(1))
代码运行结果包含:
ZeroDivisionError: integer division or modulo by zero
错误可以由 try
和 except
语句来处理。
注意,在函数调用中的 try
语句块中,发生的所有错误都会被捕捉。
def spam(divideBy):
try:
return 42 / divideBy
except ZeroDivisionError:
print('Error: Invalid argument.')
print(spam(12))
print(spam(0))
print(spam(1))
上面代码的运行结果为:
3.5
Error: Invalid argument.
None
42.0
def spam(divideBy):
return 42 / divideBy
try:
print(spam(12))
print(spam(0))
print(spam(1))
except ZeroDivisionError:
print('Error: Invalid argument.')
上面代码的运行结果为:
3.5
Error: Invalid argument.
print(spam(1))
未被执行是因为,一旦执行跳到 except
子句的代码,就不会回到 try
子句。它会继续照常向下执行。
3.8 一个小程序:之字形(Zigzag)
# zigzag.py
import time, sys
indent = 0 # How many spaces to indent.
indentIncreasing = True # Whether the indentation is increasing or not.
try:
while True: # The main program loop.
print(' ' * indent, end='')
print('********')
time.sleep(0.1) # Pause for 1/10 of a second.
if indentIncreasing:
# Increase the number of spaces:
indent = indent + 1
if indent == 20:
# Change direction:
indentIncreasing = False
else:
# Decrease the number of spaces:
indent = indent - 1
if indent == 0:
# Change direction:
indentIncreasing = True
except KeyboardInterrupt:
sys.exit()
该程序将创建来回的之字形图案,直到用户通过按Mu编辑器的“停止”按钮或按CTRL-C停止它为止。
3.9 习题
1.为什么在程序中加入函数会有好处?
解: 函数是将代码逻辑分组的主要方式。因为函数中的变量存在于它们自己的局部作用域内,所以一个函数中的代码不能直接影响其他函数中变量的值。这限制了哪些代码才能改变变量的值,对调试代码很有帮助。
函数是很好的工具,帮助你组织代码。可以认为他们是黑盒。它们以参数的形式接收输入,以返回值的形式产生输出。它们内部的代码不会影响其他函数中的变量。
3.10 实践项目
Collatz 序列
编写一个名为 collatz()
的函数,该函数有一个名为 number
的参数。如果 number
是偶数,那么 collatz()
应该打印 number // 2
,并返回该值。如果 number
是奇数,那么 collatz()
应该打印并返回 3 * number + 1
。
然后编写一个程序,让用户输入一个整数,并一直对这个数字调用 collatz()
,直到函数返回值1。(令人惊奇的是,这个序列实际上适用于任何整数,使用这个序列,迟早会得到 1!甚至数学家也不确定为什么。你的程序正在研究所谓的 Collatz 序列 (Collatz sequence),有时也被称为“最简单的不可能的数学问题”。
记住要使用 int()
函数将 input()
的返回值转换为整数,否则,它将是一个字符串值。
提示:如果 number % 2 == 0
,则整数为偶数;如果 number % 2 == 1
,则为奇数。
这个程序的输出可能是这样的:
Enter number:
3
10
5
16
8
4
2
1
def collatz(number):
if number % 2 == 0:
temp = number // 2
else:
temp = 3 * number + 1
print(temp)
return temp
print("Enter number:")
num = int(input()) # 在 Python2 中使用 num = input() 没出现错误
while num != 1:
num = collatz(num)
输入验证
将 try
和 except
语句添加到前面的项目中,以检测用户是否输入了非整数字符串。通常,如果 int()
函数传递了一个非整数字符串,就会引发一个 ValueError
错误,如 int('puppy')
。在 except
子句中,向用户打印一条消息,说明他们必须输入整数。
def collatz(number):
if number % 2 == 0:
temp = number // 2
else:
temp = 3 * number + 1
print(temp)
return temp
try:
print("Enter number:")
num = int(input())
while num != 1:
num = collatz(num)
except ValueError: # Python2 出现 NameError。也可以直接使用 except:
print("Please input an integer!")
【python 让繁琐工作自动化】目录
学习网站:https://automatetheboringstuff.com/2e/chapter3/