Python网络爬虫技术与实战
上QQ阅读APP看书,第一时间看更新

1.7.1 函数

在实际开发中,把可能需要反复执行的代码封装为函数,然后在需要执行该段代码功能时调用封装好的函数,这样不仅可以实现代码的复用,更重要的是可以保证代码的一致性,只需要修改该函数代码便可使所有调用位置均得到体现。同时,把大任务拆分成多个函数也是分治法和模块化设计的基本思路,这样有利于将复杂问题简单化。

1.基本语法

关于函数的基本语法如下:


def 函数名([参数列表]):
    ''' 注释 '''
    函数体

其中,def是用来定义函数的关键字。定义函数时在语法上需要注意的主要问题如下。

1)不需要说明形参类型,Python解释器会根据实参的值自动推断形参类型。

2)不需要指定函数返回值类型,这由函数中return语句返回的值来确定。

3)即使该函数不需要接收任何参数,也必须保留一对空的圆括号。

4)函数头部括号后面的冒号必不可少。

5)函数体相对于def关键字必须保持一定的空格缩进。

下面举一个简单的Python函数定义的例子,它将一个字符串作为传入参数,再打印到标准显示设备上。

【例1-55】定义一个简单的Python函数


def printme( str ):
    "打印传入的字符串到标准显示设备上"
    print str
    return

【例1-56】编写函数,计算并输出斐波那契数列中小于参数n的所有值,并调用该函数。


def fib(n):
    a,b = 1,1
        while a < n:
            print(a,end='')
            a,b = b,a+b
fib(1000)

2.函数嵌套

在Python中,函数的用法多种多样,但我们平时使用的大多数是一些基本算法,对于那些复杂的函数,嵌套必不可少。所谓函数嵌套,就是指在函数中定义的函数。函数嵌套保证了代码的模块化、复用性和可靠性。

【例1-57】函数嵌套


def fun1():
    m=3
        def fun2():
            n=4
            print(m+n)
        fun2()
fun1()

在上述程序运行后,编译器会将函数fun1()的函数体存放到内存中,先不去执行,直到程序的最后一行,这时发现函数fun1()被调用,于是运行函数,令m=3,然后发现了新定义的函数fun2(),于是将fun2()放在内存中,在后续调用时,再去运行其中的赋值及打印操作。需要注意,函数内定义的函数只能在函数内调用,就像函数内定义的变量,无法在外面调用。

3.lambda函数

lambda函数又被称为匿名函数,它没有复杂的函数定义,仅由一行代码构成。

lambda函数的语法如下:


result = lambda arg1,arg2,arg3,...,argN:expression

其中,result用于接收lambda函数的结果,arg1,arg2,arg3,....,argN:指的是可选参数,用于指定要传递的参数列表,参数间使用“,”分隔。expression为必选参数,它是一个表达式,用于描述函数的功能。如果函数有参数,那么将在这个表达式中使用。

【例1-58】lambda函数的使用示例


>>> lambda x, y : x+y

x和y是函数的两个参数,冒号后面的表达式是函数的返回值,你能一眼看出这个函数是在求两个变量的和,但作为一个函数,没有名字如何使用呢?根据例1-59中所示的调用方式,我们给这个匿名函数绑定一个名字,使调用匿名函数成为可能。

【例1-59】result应用


add= lambda x, y:x+y
result = add(1,2)
print(result)

需要注意的是,在使用lambda函数时,参数可以有多个,但表达式只能有一个。而且在表达式中不能出现if、while这种非表达式语句。lambda函数使用起来很方便。

4.递归函数

如果在一个函数中直接或间接地调用了该函数自身,那么便称这个过程为递归调用。函数的递归调用是函数调用的一种特殊情况,函数调用自己,自己再调用自己,如此反复调用至某个条件得到满足,此时便不再调用,最后再一层一层地返回,直到该函数的第一次调用。

递归函数的调用过程如图1-39所示。

图1-39 递归函数的调用

需要注意的是,递归函数必须有一个明确的结束条件。每当进入更深一层的递归时,问题的规模相对于上一次递归都应减少,而且相邻两次调用要有紧密的联系,通常前一次的输出是后一次的输入。如下所示的便是一个简单的递归计算示例。

【例1-60】使用递归计算5的阶乘


def fact(n):
    If n == 1:
        return 1
    return n*fact(n-1)
print(fact(5))

上面的程序运行结果为120,在这个过程中,fact()函数调用了自身return n*fact(n-1),重复的调用,最后直到n等于0,递归结束。

5.函数参数

定义函数时圆括弧内是使用逗号分隔开的形参列表,函数可以有多个参数,也可以没有参数,但定义和调用函数时一对圆括弧必须要有,表示这是一个函数并且不接受参数。调用函数时向其传递实参,将实参的引用传递给形参的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么值就够了,函数内部的复杂逻辑被封装起来,调用者无须了解。

6.位置参数

实参位置和形参保持一致,按形参声明的先后顺序一一赋值。

7.关键字参数

关键字参数主要指调用函数时的参数传递方式,调用函数的时候以形参=实参的方式来传参,此时实参的顺序无所谓。这样可以避免用户需要牢记参数位置和顺序的麻烦,使得函数的调用和参数的传递更加灵活。

关于调用函数时的关键字参数的用法示例如下所示。

【例1-61】编写关键字参数


def func1(a, b, c):
    print(a, b, c)
func1(10, 20, 30)       # 通过位置参数传参
func1(c=30, a=10, b=20) # 通过关键字参数传参
     # 通过位置参数和关键字参数结合传参(注意关键字参数必须在位置参数的后面)
func1(10, 20, c=30)

8.默认值参数

声明函数的时候,可以给参数赋默认值。如果一个形参有默认值了,那么调用函数时这个参数就可以不用传参。如果有的形参有默认值,有的形参没有默认值,那么有默认值的形参要放在没有默认值的形参之后。值得注意的是,调用函数要保证每个参数都有值。

【例1-62】编写默认值参数


# 参数c有默认值,调用函数时c不必传参
def func2(a, b, c=0):
     print(a, b, c)   # a=100, b=200
func2(100, 200, 300)
func2(100, 200)
func2(a=100, c=200, b=150)
func2(b=110, a=220)

9.可变长度参数

声明函数的时候,在参数名前加*,可以用来同时获取多个实参的值。实质是将带*的参数变成元组,将多个实参的值作为元组的元素。

注意 如果函数中既有可变长度参数又有普通参数,那么可变长度参数必须放在普通参数后边。

【例1-63】编写可变长度参数


def yt_sum(*nums):
    print(nums)
yt_sum()
yt_sum(1)
yt_sum(1, 2)
yt_sum(1, 2, 3)
yt_sum(1, 2, 3, 4, 5)
def func5(name, age, *scores):
    print(name, scores)
func5('夏明', 18, 209)
func5('小花', 10, 20, 30)