Python网络爬虫与数据分析从入门到实践
上QQ阅读APP看书,第一时间看更新

1.3 函数及用法

定义和使用函数的目的是提升Python代码的可维护性和可重用性。在本节中,我们除了介绍定义和调用函数的常规方法,还讲解定义和调用Python函数时的诸多注意事项。

1.3.1 定义和调用函数

在Python程序中,函数(function)也叫方法(method),在其中可以封装实现某种功能的代码。在之前的范例程序中,我们已经介绍过调用Python内置函数的方式,比如调用print()函数实现输出功能。除了可以调用Python自带的函数外,在Python程序中还可以定义(即创建)自己的函数。比如,如果要在Python程序中多次执行累加和的操作,就可以定义一个函数,在其中封装通用性的累加代码,然后在需要时调用这个函数。以下的FuncDemo.py范例程序演示了函数的定义和调用。

本范例程序的第1行到第7行的代码中,我们可以看到定义Python函数的示例。在定义Python函数时,需要像第1行语句那样,通过def关键字定义名为calSum的函数,该函数携带一个名为maxNum的参数,请注意def语句也需要以冒号结尾。

第2行到第7行以缩进的方式定义了该函数的主体代码,其中以while循环实现累加和,最后需要通过第7行的return关键字返回该函数的运行结果。

定义完该函数后,第8行和第9行的代码通过两次调用该calSum函数,实现了计算从1到100和从1到50的累加和。从该范例程序可知,通过定义和调用函数,在编程过程中能有效避免编写重复性的代码。

请注意,由于在定义函数参数时,无法指定参数的类型,因此在调用函数时要确保传入的参数类型和定义时的类型一致。比如,该范例程序中第1行定义的calSum函数,它的maxNum参数是整型数据,因为在该函数中是以整型的方式使用该参数。如果调用时传入的是浮点型数据,虽然结果有些怪,但不会出现语法问题,假如像第11行那样传入字符串类型的参数,那么在调用函数时就会出现异常。

1.3.2 return关键字

在定义和调用函数的场景中,如果仅在函数体内部修改参数值,而不用return返回该参数,那么在调用之后就无法得到更新后的数值,甚至会得到None而出现错误。在下面的FuncBadUsage.py范例程序中,可以看到因不用return语句而导致的错误结果。

本范例程序的第1行代码中,用def关键字定义了名为addSaraly的函数,它有个名为currentNum的参数。在函数体中的第2行,执行了给currentNum变量加1000的运算。在这种情况下,在第4行调用addSaraly函数时,打印的结果是None,这是因为addSaraly函数没有通过return语句返回结果。此时如果删除第3行的注释前导符“#”,就可以用return把currentNum的结果值返回调用该函数的程序段,那么在第4行调用addSaraly函数时,就能看到预期的结果,即6000。

1.3.3 递归调用函数

如果在一个函数内部调用该函数本身,这种做法叫函数的递归调用。下面的FactorialDemo.py范例程序以阶乘为例来演示函数的递归调用。

本范例程序的第1行到第4行的factorial函数里,给出了以递归调用实现阶乘的功能。具体做法是,在第2行判断num是否为1,如果是则返回1,否则在第4行递归调用factorial(num - 1)。

第5行调用factorial函数的参数是3,那么根据定义,会递归调用3* factorial(2),而factorial(2)则会递归调用2* factorial(1),由于factorial(1)有明确的返回值1,递归结束,随后向上推导factorial(2)等于2,而3* factorial(2)等于6,由此得到最终结果。

在函数中引入递归可以提升代码的可读性,不过在实现递归时务必注意两点:

● 第一,在函数里一定需要明确定义递归的结束条件,比如在上述范例程序中,通过第2行和第3行代码的定义,当num等于1时,递归调用结束,返回1。如果没有结束条件,那就会出现无限递归的情况。

● 第二,计算机操作系统支持的递归层数是有限的,如果递归层数过多,就会出现异常,从而中止程序的运行。

为了避免因递归导致的异常,不少项目组会在使用函数时禁用递归,或者定义一个比较小的阈值,比如只有当明确知道递归层数小于5,才能使用递归。在禁用递归的场景里,一般可以通过循环来实现相同的功能,比如下面的FactorialDemo 1.py范例程序通过循环实现了阶乘。