前面我们写的Python程序代码使用了Python语言自身提供的print函数和input函数,也使用了一些其它函数。这些函数的共同性质就是它们都能完成一些特定的功能,print函数用于输出功能,input函数用于输入功能。

在前面使用函数时,我们把函数看成黑盒,把输入扔进黑盒,黑盒会对输入进行处理,最后输出结果。这节课我们要打开函数的盒子,看一看函数的内部是什么样子?

案例非常简单,阿萌很快就绘制好了流程图。

根据绘制的流程图,阿萌编写了下面的代码。

代码创建了变量sum并初始化为零,然后连续9条语句求自然数1至自然数9的累加和,最后输出sum变量。
阿萌编写的代码和程序的执行结果都没有问题,但这不是最好的编程方式。因为代码的重复语句太多,代码看上去也不够简洁,需要使用更好的方法改变代码结构,避免编写大量的重复语句。
阿萌又重新绘制了流程图,他准备使用for循环结构来计算自然数10以内的累加和。

根据重新绘制的流程图,阿萌编写了使用for循环结构求自然数10以内累加和代码。

阿萌使用for循环结构求自然数10以内的累加和,程序代码看上去简洁多了,也没有了重复语句。这是因为应用Python的for循环改变了代码的结构,提高了代码的复用性。
虽然使用循环结构可以解决一些代码语句重复的问题,但是不能解决功能性代码重复的问题。

阿萌现在有点晕,因为案例2的要求是分别求自然数10和20以内的累加和。它需要编写两段for循环代码,来求自然数10和20以内的累加和。

阿萌编写的程序完成两个功能:一个功能是求自然数10以内的累加和并输出;第二个功能是求自然数20以内的累加和并输出。
这两个功能相同,代码也几乎完全相同,只是循环次数不同,这种情况就属于功能性重复,循环可以解决代码重复的问题,但解决不了功能性重复的问题。

我们可以设想一下,在上面的程序代码中,如果我们把求自然数累加和的代码单独编写为一个代码块,自然数作为代码块的一个参数传入进去,该代码块对传入的自然数求累加和,并把求和结果返回给调用这个代码块的语句,这样就解决了程序中功能性重复的问题。

在Python语言中,Python提供了函数用来解决类似上面功能性重复的问题,其实函数的作用远不止于解决功能性重复的问题,函数是对程序逻辑进行结构化或过程化的一种编程方法。
它可以将能够完成独立功能的代码块封装成易于管理的函数,这些函数可以被程序中的其它语句调用,而调用者可以把函数看成黑盒,不用考虑函数的内部结构和特性,只需传入参数和接收返回值就可以了。

在Python语言中,函数分为三部分:
第一部分是函数声明,函数声明占一行语句,声明语句开头使用def关键字,在def关键字后面空格后紧跟函数的名称,函数名称后面是一对小括号,括号内是需要传入的参数,参数可以为空;
第二部分是函数体,函数体可以占多行语句,函数体内包括函数注释和代码;
第三部分是返回语句,如果函数没有返回值可以省略返回语句。

在Python函数结构中,functionname表示函数的名称,parameters表示传入函数的参数名,多个参数之间使用英文逗号分隔。
函数_文档字符串是函数的注释文档,主要给出函数的功能、参数说明等信息(注释文档可以省略),function_suite是函数的代码,return是返回语句,如果函数没有返回值,返回语句可以省略,expression是返回的表达式或数据。
需要注意的是,函数体和返回语句要在函数声明后面对齐缩进。
阿萌了解了函数结构后,他准备使用函数来解决上面程序中求自然数累加和功能重复的问题。

上面的程序代码就比前边的程序代码简洁强大多了,它可以求任意自然数以内的累加和,而且编写的代码量也很少。程序定义了函数summation,该函数完成求给定自然数以内的累加和功能,自然数由函数的参数number指定。
代码语句调用函数时,直接写上函数名称即可,函数名称后面是一对小括号,需要传入的参数放在小括号内。因为summation函数返回传入自然数的累加和,因此print语句输出了函数的返回值。

函数给编写程序带来了便捷性,我们可以把程序的共用代码,或者说是程序的通用功能封装成函数。当需要使用函数时,只需要在代码的相应位置调用函数即可。

求自然数累加和是一个通用功能,它可以求任何自然数的累加和。因此将求自然数累加和功能的代码封装到一个函数里,程序需要求自然数累加和时,直接调用函数即可。
在函数声明中,summation是函数的名称,number是函数的参数。

函数中的参数起到了给函数传递数据的作用,函数调用者可以通过函数参数把函数内部需要的数据从外部传递过去。例如前面的代码定义了函数summation,它有一个参数number,函数需要这个参数来计算自然数的累加和。调用者调用函数时,需要传入一个自然数进去。

声明summation函数语句的参数称为形参,调用summation函数时传入的参数称为实参。例如在summation函数中,函数声明语句的number是形参,后面的代码调用summation函数传入的自然数10、20、100是实参。
这里需要注意的是,函数声明时的形参数量和调用函数时传入的实参数量要一致,声明的形参顺序和传入的实参顺序也要一致。

一般说来定义多少个形参,就需要传入多少个实参。在一些特殊情况下,函数虽然定义了形参,但在调用函数时可以不用传入实参,这就是默认参数的作用。
默认参数的意思就是给函数的形参设置一个默认值,如果在调用函数时没有传入实参,那么这个默认值将会作为实参传递给函数。默认参数可以简化函数的调用,调用者不需要传入过多的实参。

给函数设置默认参数时要遵循该参数具有共性和不变属性的规则,在特殊情况下可以用传入的实参代替默认值。
例如在一个计算银行利息的函数中,声明的参数有利率、本金和存款存期,在这三个参数中利率一般是不变的,它具有共性和不变属性,可以设置为默认参数,当因特殊情况利率发生变化时,可以传入实参来代替默认值。下面的代码给出了如何声明带有默认参数的函数。

在上面的代码中,定义了calculation函数,用于计算应付利息。该函数有三个参数,分别是principal(本金)、date(存期)、rate(利率),其中利率为默认参数,默认值是0.05(5%)。
默认参数的声明语法就是在形参名称后面用运算符“=”给形参赋值。当函数的形参被声明为默认参数后,调用函数时就可以省略该参数的传入。这里需要注意的是,被声明为默认参数的形参需要放置在不是默认参数的形参后面。
课程小结
我们在处理复杂问题时,往往需要把复杂问题分解为一些相对简单的部分,分别处理这些部分,然后用各个部分的解去构造整个问题的解。函数就是完成这样的功能,函数可以把相对独立的某个功能抽取出来,使之成为程序中的一段独立代码,并为这段代码取一个名字,做成一个函数定义,当要使用函数功能时,可以在程序中直接调用该函数。
上机练习
编写一个程序,要求用户输入两个整数,并求两数的和。程序要求:计算两数的和用函数实现,函数名称为sum,sum函数有两个形参,分别是num1和num2。程序调用sum函数计算两数的和。