2.5 函数
随着程序的复杂度增大、代码量增多,如何写出可重复使用的代码就是读者必须要思考的问题了,谁都不希望类似的功能编写多次,而函数则可以解决这样的问题。
2.5.1 基本函数体
函数是一种可复用的、用于实现相关联功能的代码块,通过函数可以降低代码的重复使用率。在前面的内容中,我们已经使用了大量的函数,如print函数、input函数等,通常我们也可将函数称为方法。
在Python中,我们通过def关键字来定义函数,示例代码如下:
函数定义的具体规则如下。
(1)函数代码块以def关键字开头,后接函数名、圆括号与冒号。
(2)函数可以接收任何参数,但参数必须放在圆括号内。
(3)函数中的代码块以冒号起始,并且要有缩进。
(4)函数的最后可以通过return关键字结束函数,并返回函数代码块执行的结果。如果不使用return关键字,则默认返回None,即空。
定义好函数后,我们可以通过函数加参数的形式来调用函数。示例代码如下:
上述代码定义了名为add并具有start_num与end_num两个参数的函数,该函数的作用就是计算start_num累加到end_num的值,并通过return关键字返回计算结果。在定义好函数后,我们通过函数名就可以调用函数了,将不同的参数传入函数以此获得不同的结果。
如果不使用函数的形式计算多个不同值之间的累加值,那么就需要重复编写多次for 循环的逻辑代码;而如果使用函数,则只需将具体的逻辑通过函数的形式进行定义,即可轻松复用多次。
2.5.2 局部作用域与全局作用域
要进一步理解函数,局部作用域与全局作用域的概念是无法绕开的。当一个函数被定义后,函数内部就是函数的局部作用域,函数内部新创建的变量被称为局部变量;而函数外部就是全局作用域,全局作用域中的变量被称为全局变量。
局部作用域与全局作用域之间存在对应的规则,下面我们通过不同的示例代码来理解这些规则。
全局作用域不可使用局部变量,示例代码如下:
如果在全局作用域中直接使用函数局部作用域中定义的局部变量,则会出现变量未定义的情况,因为全局作用域无法直接读取局部作用域中定义的变量,此时如果使用局部变量,编译器会认为当前变量不存在,便会抛出变量未定义的错误。
在局部作用域中可以读取全局变量,示例代码如下:
有时,我们在局部作用域中使用全局变量却会报错,我们看下面这段代码:
上述代码定义了function函数,在function函数中我们尝试在局部作用域中修改全局变量,此时抛出了UnboundLocalError: local variable 'variable' referenced before assignment错误,这会让很多人以为在局部作用域中不能修改全局作用域的变量,其实不是的,报错的内容是说变量variable在赋值之前被使用了。
如果我们在局部作用域中有变量赋值操作,那么Python解释器会认为该变量是局部变量。在报错代码variable = variable + 1中,Python解释器发现variable变量有赋值操作,会认为它是局部作用域中的变量,而variable变量其实并没有定义在局部作用域中,所以在使用variable变量时,就抛出了错误。
一个函数中的局部作用域不能使用其他函数中的局部变量,示例代码如下:
上述代码中定义了名为function1与function2的函数,两个函数都在自身的局部作用域中定义了相应的变量,而function2函数除定义局部变量外,还尝试读取function1中自定义的局部变量,这会抛出NameError: name 'a' is not defined错误。在Python中,不同函数所拥有的局部作用域是相互独立且不可相互访问的。
不同作用域可以使用相同名称的变量,示例代码如下:
上述代码中,不同的作用域使用了同名变量,虽然同名,但它们并不是同一个变量,这从输出结果也可以看出。
局部作用域与全局作用域之间的规则如下。
(1)全局作用域不可使用局部变量。
(2)局部作用域可以读取全局变量,但不能直接对其进行修改。
(3)一个函数中的局部作用域不能使用其他函数中的局部变量。
(4)不同作用域可以使用相同名称的变量。
2.5.3 global关键字
如果我们想在局部作用域中修改全局变量的值,就需要使用global关键字,在局部作用域中通过global关键字将局部变量声明成全局变量,此时就可以在局部作用域中修改全局变量。示例代码如下:
上述代码定义了名为add的函数,其中使用了全局变量a与全局变量b。因为我们要对其进行修改,所以在add函数的局部作用域中使用global关键字声明变量a与变量b为全局变量。从输出结果可以看出,全局变量a与全局变量b被add函数改变了。
global关键字虽然可以让用户在局部作用域中修改全局作用域中的变量,但我们并不提倡这种形式,当代码比较复杂时,过度使用global关键字会让代码逻辑变得复杂,容易出现Bug。
程序员通常将错误程序称为Bug,Bug直译为虫子,为什么要将错误程序称为虫子呢?
1949年9月9日,天气炎热,当时人们还在使用真空管制作的计算机,这种计算机通过电流来控制逻辑开关,从而实现不同的目的。这种形式的计算机会发出大量的光和热,再加上天气炎热,工作人员就将计算机所在房间的窗户全部打开通风。
可能是被房间内计算机的光线所吸引,一只飞蛾飞进了计算机的70号继电器中,导致计算机无法运行。
经过工作人员一天的排查,名为Grace Hopper的女性工作人员发现了这只飞蛾,她用自己的发夹将飞蛾夹出,并将它的尸体贴在自己的管理日志中,上面写道:“就是这个Bug(虫子),让我们一天都无法工作”,如图2.2所示。
图2.2
Grace Hopper的管理日志目前保存在美国国家历史博物馆中,从此计算机程序中出现的错误都被称为Bug,而修改程序错误的行为便被称为Fix Bug。
2.5.4 实现斐波那契数列
简单来说,若一个数列中的每项都是前两项的和,那么这个数列就是斐波那契数列,具体如下:
如何通过程序输出斐波那契数列?
仔细思考,如果要获得完整的斐波那契数列,则必须使用for循环或while循环。此外,获得完整斐波那契数列的关键在于如何实现“每项都是前两项的和”的逻辑。具体代码如下: