劳务输送网站建设方案,aspcms网络公司官方网站源码,网址导航是什么软件,wordpress crm主题原文#xff1a;https://automatetheboringstuff.com/2e/chapter3/ 您已经熟悉了前几章中的print()、input()和len()函数。Python 提供了几个这样的内置函数#xff0c;但是您也可以编写自己的函数。函数就像一个程序中的一个小程序。
为了更好地理解函数是如何工作的#… 原文https://automatetheboringstuff.com/2e/chapter3/ 您已经熟悉了前几章中的print()、input()和len()函数。Python 提供了几个这样的内置函数但是您也可以编写自己的函数。函数就像一个程序中的一个小程序。
为了更好地理解函数是如何工作的让我们创建一个函数。将该程序输入文件编辑器并保存为helloFunc.py : def hello(): # ➊print(Howdy!) # ➋print(Howdy!!!)print(Hello there.)hello() # ➌hello()hello()您可以在autbor.com/hellofunc查看该程序的执行情况。第一行是一个def语句 ➊它定义了一个名为hello()的函数。跟在def语句 ➋ 后面的代码是函数体。这段代码在函数被调用时执行而不是在函数第一次被定义时执行。*
函数 ➌ 后面的hello()行是函数调用。在代码中函数调用就是函数名后跟括号括号之间可能有一些参数。当程序执行到这些调用时它将跳转到函数的第一行并开始执行那里的代码。当它到达函数的末尾时执行返回到调用该函数的行并像以前一样继续遍历代码。
由于这个程序调用了hello()三次所以hello()函数中的代码执行了三次。当您运行该程序时输出如下所示
Howdy!
Howdy!!!
Hello there.
Howdy!
Howdy!!!
Hello there.
Howdy!
Howdy!!!
Hello there.函数的一个主要目的是对多次执行的代码进行分组。如果没有定义函数您每次都必须复制并粘贴这些代码程序看起来会像这样
print(Howdy!)
print(Howdy!!!)
print(Hello there.)
print(Howdy!)
print(Howdy!!!)
print(Hello there.)
print(Howdy!)
print(Howdy!!!)
print(Hello there.)一般来说您总是希望避免重复代码因为如果您决定更新代码——例如如果您发现了一个需要修复的 BUG 您将不得不记住在您复制代码的任何地方更改代码。
随着你获得更多的编程经验你会经常发现自己删除重复代码这意味着去掉重复的或复制粘贴的代码。重复数据删除使您的程序更短、更易读、更易于更新。
带参数的def语句
当您调用print()或len()函数时您通过在括号之间键入值来传递它们称为参数。您也可以定义自己的接受参数的函数。将此示例输入文件编辑器并保存为helloFunc2.py : def hello(name): # ➊print(Hello, name) # ➋hello(Alice) # ➌hello(Bob)当您运行该程序时输出如下所示
Hello, Alice
Hello, Bob您可以在autbor.com/hellofunc2查看该程序的执行情况。本程序中hello()函数的定义有一个参数叫做name➊。参数是包含参数的变量。当用参数调用函数时参数存储在形参中。第一次调用hello()函数时它被传递给参数Alice➌。程序执行进入函数参数name自动设置为Alice这是由print()语句 ➋ 打印出来的。
关于参数需要特别注意的一点是当函数返回时存储在参数中的值会被遗忘。例如如果你在前面的程序中在hello(Bob)后面添加了print(name)程序会给你一个NameError因为没有名为name的变量。这个变量在函数调用hello(Bob)返回后被销毁所以print(name)会引用一个不存在的name变量。
这类似于当程序终止时程序的变量被遗忘。在本章后面当我讨论什么是函数的局部作用域时我会更多地讨论为什么会发生这种情况。
定义、调用、传递、实参、形参
术语定义、调用、传递、实参、形参可能会混淆。让我们看一个代码示例来回顾这些术语 def sayHello(name): # ➊print(Hello, name)sayHello(Al) # ➋定义函数就是创建函数就像赋值语句创建变量一样。def语句定义了sayHello()函数 ➊。sayHello(Al)行 ➋ 调用现在创建的函数将执行发送到函数代码的顶部。这个函数调用也被称为将字符串值Al传递给函数。在函数调用中传递给函数的值是实参。参数Al被分配给一个名为name的局部变量。被赋予实参的变量是形参。
很容易混淆这些术语但保持它们的一致性将确保你准确地知道本章文本的意思。
返回值和返回语句
当您调用len()函数并给它传递一个参数比如Hello时函数调用会计算出整数值5这是您传递给它的字符串的长度。一般来说函数调用求值的值被称为函数的返回值。
当使用def语句创建函数时可以用return语句指定返回值应该是什么。一份return语句由以下内容组成
return关键字函数应该返回的值或表达式
当一个表达式与一个return语句一起使用时返回值就是这个表达式计算的值。例如下面的程序定义了一个函数该函数根据作为参数传递的数字返回不同的字符串。将此代码输入文件编辑器并保存为magic8Ball.py : import random # ➊def getAnswer(answerNumber): # ➋if answerNumber 1: # ➌return It is certainelif answerNumber 2:return It is decidedly soelif answerNumber 3:return Yeselif answerNumber 4:return Reply hazy try againelif answerNumber 5:return Ask again laterelif answerNumber 6:return Concentrate and ask againelif answerNumber 7:return My reply is noelif answerNumber 8:return Outlook not so goodelif answerNumber 9:return Very doubtfulr random.randint(1, 9) # ➍fortune getAnswer(r) # ➎print(fortune) # ➏您可以在autbor.com/magic8ball查看该程序的执行情况。当这个程序启动时Python 首先导入random模块 ➊。然后定义getAnswer()函数 ➋。因为函数正在被定义而不是被调用所以执行会跳过其中的代码。接下来用两个参数调用random.randint()函数1和9➍。它求值为一个在1和9之间的随机整数包括1和9本身这个值存储在一个名为r的变量中。
使用r作为参数 ➎ 调用getAnswer()函数。程序执行移动到getAnswer()函数 ➌ 的顶部值r存储在名为answerNumber的参数中。然后根据answerNumber中的值该函数返回许多可能的字符串值之一。程序执行返回到程序底部原来调用getAnswer()➎ 的那一行。返回的字符串被赋给一个名为fortune的变量然后该变量被传递给一个print()调用 ➏ 并被打印到屏幕上。
请注意由于可以将返回值作为参数传递给另一个函数调用因此可以缩短这三行代码
r random.randint(1, 9)
fortune getAnswer(r)
print(fortune)到这条等价的线
print(getAnswer(random.randint(1, 9)))记住表达式是由值和运算符组成的。函数调用可以在表达式中使用因为调用计算其返回值。
None值
在 Python 中有一个值叫做None代表没有值。None值是NoneType数据类型的唯一值。其他编程语言可能会将这个值称为null、nil或undefined。就像布尔值True和False一样None必须用大写的N来键入。
当您需要在变量中存储不会与实值混淆的内容时这种不带值的值会很有帮助。使用None的一个地方是作为print()的返回值。print()函数在屏幕上显示文本但它不需要像len()或input()那样返回任何内容。但是由于所有的函数调用都需要计算返回值print()返回None。要查看这一过程请在交互式 Shell 中输入以下内容 spam print(Hello!)
Hello!None spam
True在幕后Python 将return None添加到任何没有return语句的函数定义的末尾。这类似于while或for循环如何以continue语句隐式结束。此外如果使用不带值的return语句也就是说只有return关键字本身那么将返回None。
关键字参数和print()函数
大多数参数由它们在函数调用中的位置来标识。比如random.randint(1, 10)和random.randint(10, 1)不一样。函数调用random.randint(1, 10)将返回一个在1和10之间的随机整数因为第一个参数是区间的低端第二个参数是高端而random.randint(10, 1)会导致错误。
然而关键字参数不是通过它们的位置而是通过在函数调用中放在它们前面的关键字来识别的。关键字参数常用于可选参数。例如print()函数有可选参数end和sep来分别指定应该在它的参数末尾和参数之间打印什么分隔它们。
如果您用以下代码运行了一个程序
print(Hello)
print(World)输出如下所示
Hello
World两个输出的字符串出现在不同的行上因为print()函数会自动在传递的字符串末尾添加一个换行符。但是您可以设置end关键字参数将换行符更改为不同的字符串。例如如果代码是这样的
print(Hello, end)
print(World)输出如下所示
HelloWorld输出被打印在一行上因为在Hello之后不再打印换行符。相反打印空白字符串。如果您需要禁用添加到每个print()函数调用末尾的换行符这很有用。
同样当你传递多个字符串值给print()时函数会自动用一个空格把它们分开。在交互式 Shell 中输入以下内容 print(cats, dogs, mice)
cats dogs mice但是您可以通过向sep关键字参数传递一个不同的字符串来替换默认的分隔字符串。在交互式 Shell 中输入以下内容 print(cats, dogs, mice, sep,)
cats,dogs,mice您也可以在自己编写的函数中添加关键字参数但是首先您必须在接下来的两章中了解列表和字典数据类型。现在只需要知道一些函数有可选的关键字参数可以在调用函数时指定。
调用栈
想象你和某人进行了一次曲折的对话。你谈到了你的朋友爱丽丝这让你想起了一个关于你同事鲍勃的故事但首先你必须解释一下你的表妹卡罗尔。你写完关于卡罗尔的故事后继续谈论鲍勃当你写完关于鲍勃的故事后继续谈论爱丽丝。但这时你想起了你的哥哥大卫所以你讲了一个关于他的故事然后继续完成你原来关于爱丽丝的故事。你的对话遵循一个类似栈的结构就像图 3-1 中那样。对话是栈式的因为当前主题总是在栈的顶部。 图 3-1你曲折的对话栈
类似于我们曲折的对话调用一个函数不会将执行单向发送到函数的顶部。Python 会记住哪一行代码调用了这个函数这样当执行遇到一个return语句时就可以返回那里。如果那个原始函数调用了其他函数在从原始函数调用返回之前执行将首先返回到那些函数调用。
打开文件编辑器窗口输入以下代码保存为abcdCallStack.py : def a():print(a() starts)b() # ➊d() # ➋print(a() returns)def b():print(b() starts)c() # ➌print(b() returns)def c():print(c() starts) # ➍print(c() returns)def d():print(d() starts)print(d() returns)a() # ➎如果运行这个程序输出将如下所示
a() starts
b() starts
c() starts
c() returns
b() returns
d() starts
d() returns
a() returns您可以在autbor.com/abcdcallstack查看该程序的执行情况。当a()被调用 ➎ 时它调用b()➊后者又调用c()➌。c()函数不调用任何东西它只显示c() starts、➍ 和c() returns然后返回到b()中称其为 ➌ 的行。一旦执行返回到b()中调用c()的代码它就返回到a()中调用b()➊ 的行。执行继续到b()函数 ➋ 中的下一行这是对d()的调用。和c()函数一样d()函数也不调用任何东西。在返回到调用它的b()中的行之前它只显示d() starts和d() returns。由于b()不包含其他代码执行返回到a()中调用b()➋ 的行。在程序 ➎ 结束返回到原来的a()调用之前a()中的最后一行显示a() returns。
调用栈是 Python 在每次函数调用后记住返回执行结果的方式。调用栈不存储在程序的变量中相反Python 在幕后处理它。当你的程序调用一个函数时Python 会在调用栈顶创建帧对象。帧对象存储原始函数调用的行号以便 Python 可以记住返回到哪里。如果进行了另一个函数调用Python 会将另一个帧对象放在调用栈中的另一个之上。
当函数调用返回时Python 从栈顶移除一个帧对象并将执行移动到存储在其中的行号。请注意帧对象总是从栈顶部添加和移除而不是从任何其他位置。图 3-2 展示了abcdCallStack.py中调用栈在每个函数被调用并返回时的状态。 图 3-2调用栈的帧对象为abcdCallStack.py调用并从函数*返回
调用栈的顶部是执行当前所在的函数。当调用栈为空时在所有函数之外的行上执行。
调用栈是一个技术细节在编写程序时你并不一定需要知道它。理解函数调用返回到它们被调用的行号就足够了。然而理解调用栈会使理解局部和全局作用域变得更容易这将在下一节中描述。
局部和全局作用域
在被调用函数中赋值的参数和变量被认为存在于该函数的局部作用域中。在所有函数之外赋值的变量被认为存在于全局作用域中。存在于局部作用域内的变量称为局部变量而存在于全局作用域内的变量称为全局变量。变量必须是其中之一它不可能既是局部的又是全局性的。
将作用域视为变量的容器。当作用域被销毁时作用域变量中存储的所有值都会被遗忘。只有一个全局作用域它是在程序开始时创建的。当你的程序终止时全局作用域被破坏它的所有变量都被遗忘。否则下次运行程序时变量会记住上次运行时的值。
每当调用一个函数时就会创建一个局部作用域。函数中分配的任何变量都存在于函数的局部作用域内。当函数返回时局部作用域被破坏这些变量被遗忘。下次调用该函数时局部变量将不会记得上次调用该函数时存储在其中的值。局部变量也存储在调用栈上的帧对象中。
作用域的重要性有几个原因
所有函数之外的全局作用域内的代码不能使用任何局部变量。但是局部作用域内的代码可以访问全局变量。函数局部作用域内的代码不能使用任何其他局部作用域内的变量。如果不同的变量在不同的作用域内可以使用相同的名称。也就是说可以有一个名为spam的局部变量和一个名为spam的全局变量。
Python 之所以有不同的作用域而不是把所有东西都变成全局变量是因为当代码在对函数的特定调用中修改变量时函数只能通过它的参数和返回值与程序的其余部分进行交互。这缩小了可能导致错误的代码行数。如果你的程序除了全局变量之外什么都不包含并且因为一个变量被设置为错误的值而出现了一个 bug那么就很难找到这个错误的值是在哪里设置的。它可以在程序中的任何地方设置你的程序可能有几百或几千行长但是如果 BUG 是由一个具有错误值的局部变量引起的那么您知道只有那个函数中的代码可能设置错误。
虽然在小程序中使用全局变量是好的但是随着程序变得越来越大依赖全局变量是一个坏习惯。
局部变量不能在全局作用域内使用
考虑这个程序当您运行它时它将导致一个错误
def spam():eggs 31337 # ➊
spam()
print(eggs)如果运行这个程序输出将如下所示
Traceback (most recent call last):File C:/test1.py, line 4, in moduleprint(eggs)
NameError: name eggs is not defined发生错误是因为eggs变量只存在于当spam()被调用 ➊ 时创建的局部作用域中。一旦程序执行从spam返回这个局部作用域就被破坏了不再有一个名为eggs的变量。所以当你的程序试图运行print(eggs)时Python 会给你一个错误说eggs没有定义。如果你仔细想想这是有道理的当程序在全局作用域内执行时不存在局部作用域所以不可能有任何局部变量。这就是为什么在全局作用域内只能使用全局变量。
局部作用域不能使用其他局部作用域中的变量
每当调用一个函数时包括从另一个函数调用一个函数时都会创建一个新的局部作用域。考虑这个程序 def spam():eggs 99 # ➊bacon() # ➋print(eggs) # ➌def bacon():ham 101eggs 0 # ➍spam() # ➎您可以在autbor.com/otherlocalscopes查看该程序的执行情况。当程序启动时spam()函数被调用 ➎一个局部作用域被创建。本地变量eggs➊ 被设置为99。然后调用bacon()函数 ➋并创建第二个局部作用域。多个本地作用域可以同时存在。在这个新的局部作用域中局部变量ham被设置为101并且一个局部变量eggs——不同于spam()的局部作用域中的那个——也被创建 ➍ 并被设置为0。
当bacon()返回时该调用的局部作用域被销毁包括它的eggs变量。程序在spam()函数中继续执行打印eggs➌ 的值。由于调用spam()的局部作用域仍然存在唯一的eggs变量是spam()函数的eggs变量它被设置为99。这是程序打印的内容。
结果是一个函数中的局部变量与另一个函数中的局部变量完全分离。
可以从局部作用域读取全局变量
考虑以下程序
def spam():print(eggs)
eggs 42
spam()
print(eggs)您可以在autbor.com/readglobal查看该程序的执行情况。由于在spam()函数中没有名为eggs的参数或者任何给eggs赋值的代码所以当eggs在spam()中使用时Python 认为它是对全局变量eggs的引用。这就是为什么运行前一个程序时会打印出42。
局部和全局变量同名
从技术上讲在 Python 中不同作用域的全局变量和局部变量使用相同的变量名是完全可以接受的。但是为了简化你的生活避免这样做。要查看发生了什么请在文件编辑器中输入以下代码并将其保存为localGlobalSameName.py: def spam():eggs spam local # ➊print(eggs) # prints spam localdef bacon():eggs bacon local # ➋print(eggs) # prints bacon localspam()print(eggs) # prints bacon localeggs global # ➌bacon()print(eggs) # prints global当您运行该程序时它会输出以下内容
bacon local
spam local
bacon local
global您可以在autbor.com/localglobalsamename查看该程序的执行情况。这个程序中实际上有三个不同的变量但令人困惑的是它们都被命名为eggs。这些变量如下
一个名为eggs的变量当spam()被调用时它存在于一个局部作用域内。 # ➊
一个名为eggs的变量当bacon()被调用时它存在于一个局部作用域内。 # ➋
全局作用域内存在的一个名为eggs的变量。 # ➌
因为这三个独立的变量都有相同的名称所以在任何给定的时间跟踪哪个变量被使用可能会很混乱。这就是为什么您应该避免在不同的作用域中使用相同的变量名。
全局语句
如果你需要在一个函数中修改一个全局变量使用global语句。如果你在一个函数的顶部有一行比如global eggs它告诉 Python“在这个函数中eggs指的是全局变量所以不要用这个名字创建一个局部变量”。例如在文件编辑器中输入以下代码并将其保存为globalStatement.py :
def spam():global eggs # ➊eggs spam # ➋eggs global
spam()
print(eggs)当您运行这个程序时最后一个print()调用将输出如下内容
spam您可以在autbor.com/globalstatement查看该程序的执行情况。因为eggs在spam()➊ 的顶部被语句为global所以当eggs被设置为spam➋ 时这个赋值是对全局作用域eggs完成的。没有创建本地eggs变量。
有四个规则来区分变量是在局部作用域内还是在全局作用域内
如果一个变量在全局作用域内使用即在所有函数之外那么它总是一个全局变量。如果在一个函数中有一个针对该变量的global语句那么它就是一个全局变量。否则如果变量在函数的赋值语句中使用它就是局部变量。但是如果变量没有在赋值语句中使用它就是一个全局变量。
为了更好地理解这些规则这里有一个示例程序。在文件编辑器中输入以下代码并将其保存为sameNameLocalGlobal.py :
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
spam()
print(eggs)在spam()函数中eggs是全局eggs变量因为在函数 ➊ 的开头有一个eggs的global语句。在bacon()中eggs是一个局部变量因为在函数 ➋ 中有一个赋值语句。在ham()➌ 中eggs是全局变量因为在该函数中没有赋值语句或global语句。如果您运行sameNameLocalGlobal.py输出将如下所示
spam在autbor.com/sameNameLocalGlobal可以查看该程序的执行情况。在函数中变量要么总是全局的要么总是局部的。函数中的代码不能使用名为eggs的局部变量然后在同一个函数中使用全局变量eggs。 注 如果你想从一个函数中修改存储在一个全局变量中的值你必须在那个变量上使用一个全局语句。 如果在给一个函数赋值之前试图在函数中使用一个局部变量就像下面的程序一样Python 会给出一个错误。要查看这一点请在文件编辑器中输入以下内容并将其保存为sameNameError.py : def spam():print(eggs) # ERROR!eggs spam local # ➊eggs global # ➋spam()如果运行前面的程序它会产生一条错误消息。
Traceback (most recent call last):File C:/sameNameError.py, line 6, in modulespam()File C:/sameNameError.py, line 2, in spamprint(eggs) # ERROR!
UnboundLocalError: local variable eggs referenced before assignment您可以在autbor.com/sameNameError查看该程序的执行情况。发生这个错误是因为 Python 看到在spam()函数 ➊ 中有一个针对eggs的赋值语句因此认为eggs是局部的。但是因为print(eggs)是在eggs被赋值之前执行的所以局部变量eggs并不存在。Python 将退回到使用全局eggs变量 ➋。
起到“黑匣子”的作用
通常关于一个函数你需要知道的只是它的输入参数和输出值您不必总是为函数代码的实际工作方式而烦恼。当您以这种高级方式考虑函数时通常会说您正在将函数视为“黑盒”
这个想法是现代编程的基础。本书后面的章节将向你展示几个模块这些模块的函数是由其他人编写的。如果您好奇的话可以看一眼源代码但是您不需要知道这些函数是如何工作的才能使用它们。因为鼓励编写没有全局变量的函数所以通常不必担心函数的代码与程序的其他部分相互影响。
异常处理
现在在你的 Python 程序中得到一个错误或者说异常意味着整个程序将会崩溃。您不希望这种情况发生在真实世界的程序中。相反您希望程序检测错误处理它们然后继续运行。
例如考虑下面的程序它有一个被零除的错误。打开文件编辑器窗口输入以下代码保存为zeroDivide.py :
def spam(divideBy):return 42 / divideBy
print(spam(2))
print(spam(12))
print(spam(0))
print(spam(1))我们已经定义了一个名为spam的函数给它一个参数然后打印带有各种参数的函数值看看会发生什么。这是您运行前面的代码时得到的输出
21.0
3.5
Traceback (most recent call last):File C:/zeroDivide.py, line 6, in moduleprint(spam(0))File C:/zeroDivide.py, line 2, in spamreturn 42 / divideBy
ZeroDivisionError: division by zero您可以在autbor.com/zerodivide查看该程序的执行情况。每当你试图将一个数除以零时就会发生一个ZeroDivisionError。根据错误消息中给出的行号您知道spam()中的return语句导致了一个错误。
可以用try和except语句处理错误。可能有错误的代码放在一个try子句中。如果发生错误程序执行移动到下一个except子句的开始。
您可以将之前被零除的代码放在一个try子句中并让一个except子句包含代码来处理这个错误发生时会发生什么。
def spam(divideBy):try:return 42 / divideByexcept ZeroDivisionError:print(Error: Invalid argument.)
print(spam(2))
print(spam(12))
print(spam(0))
print(spam(1))当try子句中的代码导致错误时程序立即执行到except子句中的代码。运行该代码后执行照常继续。前面程序的输出如下
21.0
3.5
Error: Invalid argument.
None
42.0您可以在autbor.com/tryexceptzerodivide查看该程序的执行情况。注意在try块中的函数调用中出现的任何错误也会被捕获。考虑下面的程序它在try块中有spam()个调用
def spam(divideBy):return 42 / divideBy
try:print(spam(2))print(spam(12))print(spam(0))print(spam(1))
except ZeroDivisionError:print(Error: Invalid argument.)运行该程序时输出如下所示
21.0
3.5
Error: Invalid argument.您可以在autbor.com/spamintry查看该程序的执行情况。永远不执行print(spam(1))的原因是一旦执行跳转到except子句中的代码就不会返回到try子句。相反它只是像平常一样继续向下移动程序。
一个短程序之字形
让我们用你到目前为止学到的编程概念来创建一个小的动画程序。该程序将创建一个来回的锯齿形图案直到用户通过按下 Mu 编辑器的停止按钮或按下CTRL-C来停止它。当您运行该程序时输出将类似于以下内容 ********************************
****************************************在文件编辑器中键入以下源代码并将文件保存为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 1if indent 20:# Change direction:indentIncreasing Falseelse:# Decrease the number of spaces:indent indent - 1if indent 0:# Change direction:indentIncreasing True
except KeyboardInterrupt:sys.exit()让我们从顶部开始逐行查看这段代码。
import time, sys
indent 0 # How many spaces to indent.
indentIncreasing True # Whether the indentation is increasing or not.首先我们将导入time和sys模块。我们的程序使用两个变量indent变量记录八个星号之前缩进了多少个空格而indentIncreasing包含一个布尔值来确定缩进量是增加还是减少。
try:while True: # The main program loop.print( * indent, end)print(********)time.sleep(0.1) # Pause for 1/10 of a second.接下来我们将程序的其余部分放在一个 try 语句中。当用户在 Python 程序运行时按下CTRL-C时Python 会引发KeyboardInterrupt异常。如果没有try - except语句来捕捉这个异常程序就会崩溃并显示一条难看的错误消息。然而对于我们的程序我们希望它通过调用sys.exit()来干净地处理KeyboardInterrupt异常。代码在程序末尾的except语句中。
无限循环将永远重复我们程序中的指令。这包括使用 * indent打印正确的缩进空间量。我们不想在这些空格后自动打印一个换行符所以我们也将end传递给第一个print()调用。第二个print()调用打印星号带。time.sleep()函数还没有被介绍但是可以说它在我们的程序中引入了十分之一秒的暂停。 if indentIncreasing:# Increase the number of spaces:indent indent 1if indent 20:indentIncreasing False # Change direction.接下来我们要调整下次打印星号时的缩进量。如果indentIncreasing是True那么我们要给indent加一。但是一旦缩进量达到20我们希望缩进量减少。 else:# Decrease the number of spaces:indent indent - 1if indent 0:indentIncreasing True # Change direction.同时如果indentIncreasing是False我们要从indent中减去一。一旦缩进量达到0我们希望缩进量再次增加。无论哪种方式程序执行都将跳回到主程序循环的开始再次打印星号。
except KeyboardInterrupt:sys.exit()如果用户在try程序块中的任何一点按下CTRL-C则KeyboardInterrrupt异常被引发并由该except语句处理。程序执行在except块内移动运行sys.exit()并退出程序。这样即使主程序循环是一个无限循环用户也有办法关闭程序。
总结
函数是将代码划分成逻辑组的主要方式。由于函数中的变量存在于它们自己的局部作用域内所以一个函数中的代码不能直接影响其他函数中变量的值。这限制了哪些代码可能会更改变量的值这对调试代码很有帮助。
函数是帮助你组织代码的一个很好的工具。你可以把它们想象成黑盒它们有参数形式的输入和返回值形式的输出其中的代码不会影响其他函数中的变量。
在前几章中一个错误就可能导致你的程序崩溃。在本章中你学习了try和except语句它们可以在检测到错误时运行代码。这可以使你的程序对常见的错误更有弹性。
练习题 为什么函数在你的程序中有优势 函数中的代码什么时候执行定义函数的时候还是调用函数的时候 哪个语句创建了一个函数 函数和函数调用的区别是什么 一个 Python 程序中有多少个全局作用域多少个本地示波器 当函数调用返回时局部作用域内的变量会发生什么 什么是返回值返回值可以是表达式的一部分吗 如果一个函数没有返回语句那么调用这个函数的返回值是什么 你怎么能强迫一个函数中的变量引用全局变量呢 None的数据类型是什么 import areallyourpetsnamederic语句是做什么的 如果你在一个名为spam的模块中有一个名为bacon()的函数导入spam后你会如何调用它 当程序出错时如何防止它崩溃 try子句中包含什么except子句中包含什么
实践项目
为了练习编写程序来完成以下任务。
编写一个名为collatz()的函数它有一个名为number的参数。如果number是偶数那么collatz()应该打印number // 2并返回这个值。如果number是奇数那么collatz()应该打印并返回3 * number 1。
然后编写一个程序让用户输入一个整数并一直调用这个数字的collatz()直到函数返回值1。令人惊讶的是这个序列实际上适用于任何整数——迟早使用这个序列你会得到 1甚至数学家也不确定为什么。你的程序正在探索所谓的 Collatz 序列有时被称为“最简单的不可能的数学问题”
记得用int()函数把input()的返回值转换成整数否则它将是字符串值。
提示整数number如果number % 2 0是偶数如果number % 2 1是奇数。
该程序的输出可能如下所示
Enter number:
3
10
5
16
8
4
2
1输入验证
将try和except语句添加到前面的项目中以检测用户是否键入了非整数字符串。通常情况下int()函数如果被传递了一个非整数字符串就会引发一个ValueError错误就像在int(puppy)中一样。在except子句中打印一条消息给用户告诉他们必须输入一个整数。*