Python函数详解

发表时间: 2023-03-09 22:38

一、函数简介

1.1 作用

函数的主要作用就是将逻辑上可看作一个整体的多条语句进行封装,从而提高内聚性及代码的复用。其实,Python的函数就相当于Java中的方法。

1.2 使用步骤

函数定义和调用的语法如下:

# 函数的定义def 函数名(参数):    代码1    代码2    ...# 函数的调用函数名(参数)

需要说明的是,函数可没有参数,此时即所谓的无参函数。此外,函数必须先定义后使用。

下面以计算两个数的和为例进行演示:

# 声明函数def add_num(a, b):    return a + b# 进行调用print(f"3 + 4的和为:{add_num(3, 4)}")

执行结果如下:

1.3 函数的说明文档

1.3.1 声明

函数的说明文档就是对该函数用途的描述。函数文档的语法格式如下:

def 函数名(参数)    """说明文档"""    代码    ....

下面给出对应的示例:

# 声明函数def add_num(a, b):    """    用于计算两个数的和    :param a: a    :param b: b    :return: 两数之和    """    return a + b

1.3.2 查看

函数的描述文档通过help函数进行查看,其语法格式为:

help(函数名)

下面为对应的示例:

# 声明函数def add_num(a, b):    """    用于计算两个数的和    :param a: a    :param b: b    :return: 两数之和    """    return a + bhelp(add_num)

执行结果如下:

1.4 函数嵌套

所谓函数嵌套是指一个函数在执行过程中调用了其他的函数。其语法格式为:

def 函数A(参数):    代码    ...    def 函数B(参数):    函数A(参数)    代码    ...

下面给出对应的示例:

def test_a():    print("------- test_a start -------")    print("------- test_a 执行的代码 -------")    print("------- test_a end -------")def test_b():    print("------- test_b start -------")    test_a()    print("------- test_b end -------")test_b()

执行结果如下:

二、变量的作用域

所谓变量的作用域是指变量生效范围。主要分为两类,分别为:局部变量和全局变量。

2.1 局部变量

局部变量即定义在函数内部的变量,该变量只在函数内部生效。若试图在函数外访问局部变量,就会报错。比如下面的示例:

def test_a():    a = 100    print(f"test_a内部访问局部变量a的结果为:{a}")test_a()print(f"试图在函数外访问局部变量a的结果为:{a}")

执行结果如下:

2.2 全局变量

所谓全局变量就是在函数内部和外部都能被访问的变量。比如下面的例子:

a = 100def test_a():    print(f"函数test_a内访问全局变量a的结果为:{a}")    def test_b():    print(f"函数test_b内访问全局变量a的结果为:{a}")test_a()test_b()

执行结果如下:

那么能否在函数内修改全局变量呢?当然可以,只不过在函数内需使用global关键字来提取进行声明,否则修改不会生效。比如下面的示例:

a = 100def test_a():    a = 200    print("试图在test_a函数中修改全局变量a")def test_b():    global a    a = 300    print("试图在test_b函数中修改全局变量a")test_a()print(f"执行test_a函数后,变量a的结果为:{a}")test_b()print(f"执行test_b函数后,变量a的结果为:{a}")    

执行结果如下:

三、函数的返回值

当函数中只声明一条返回语句时,该函数的返回值的就是该return语句指定的值。但若声明多条返回语句时,生效的是首条return语句。如下面的例子:

def return_num():    return 1    return 2result = return_num()print(f"result的值为:{result}")

执行结果如下:

需要说明的是,若在Java方法中试图声明多条return语句时,则无法通过编译。

那么,若确有返回多个值的需求时,可将待返回的多个值用逗号进行间隔即可,此时Python会自动将return指定的多个返回值封装为元组类型后进行返回。比如下面的示例:

def return_num():    return 1, 2result = return_num()print(f"result的值为:{result}")print(f"result的类型为:{type(result)}")

执行结果如下:

当然,除使用该方式外,还可将待返回的多个值显式声明为列表、元组或字典。

四、函数的参数

下面将简单介绍一下函数的参数。

4.1 引用

在介绍函数参数前先说一下引用。所谓引用其实可理解为变量对应的内存编号。这点与Java的引用保持一致。而函数在调用时,其参数皆通过引用来进行传递。

下面给出一个例子:

def test1(a):    print(f"a做加法运算前的值为:{a}")    print(f"a做加法运算 前的标识为:{id(a)}")    a += a    print(f"a做加法运算后的值为:{a}")    print(f"a做加法运算后的标识为:{id(a)}")# 整型计算b = 100test1(b)# 列表计算c = ["Tom", "Jerry", "Spike"]test1(c)

上述例子的id函数用于获取某个对象的唯一标识值,感觉与Java中的System.idenfityHashCode(Object x)方法的效果一样。

执行结果如下:

当然,对于传递的引用来说大致可分为可变类型和不可变类型两种。其区别在于数据是否可被修改。常见的可变类型有:列表、集合和字典;常见的不可变类型有:整型、浮点型、字符串和元组。

下面给出对应的例子:

def change(a, val):    print(f"操作前a的标识:{id(a)}")    a += val    print(f"操作后a的标识:{id(a)}")# 整数print("整数进行加法运算:")n = 10change(n, 2)# 字符串print("字符串进行加法运算:")s = "hello"change(s, "world")# 列表print("列表进行加法运算:")l = ["Tom", "Jerry"]change(l, ["Spike"])

执行结果如下:

4.2 位置参数

所谓位置参数是指调用函数时会根据函数的位置来传递相关参数。需要说明的是,传递参数时,传递顺序必须与参数定义顺序保持一致。

下面给出对应的示例:

def user_info(name, age, gender):    print(f"名字是:{name}, 年龄是:{age}, 性别为:{gender}")user_info("Tom", 20, "男")

执行结果如下:

4.3 关键字参数

所谓关键字参数是指,函数调用时通过类似"键=值"的形式来指定对应参数。该种参数指定方式,语义更清晰,使用也更方便。

需要说明的是,当使用关键字参数时,其必须放在位置参数的后面。当然,关键字参数之间不存在先后顺序。

下面给出对应的例子:

def user_info(name, age, gender):    print(f"名字是:{name}, 年龄是:{age}, 性别为:{gender}")user_info("Tom", age=20, gender="男")user_info("Jerry", gender="女", age=21)

执行结果如下:

4.4 缺省参数

缺省参数也叫默认参数,主要用于为函数的参数提供默认值,当然,前提是调用函数时未指定该参数时。需要说明的是,缺省参数必须放在位置参数后面。

下面给出对应的示例:

def user_info(name, age, gender="男"):    print(f"名字是:{name}, 年龄是:{age}, 性别为:{gender}")user_info("Tom", 20)user_info("Jerry",21, "女")

执行结果如下:

4.5 可变参数

可变参数也叫不定长参数,若在调用函数时无法确定参数的个数,此时就可使用可变参数。当然,在Python中叫包裹参数,其大概分为两类,分别是:包裹位置参数和包裹关键字参数。其中,包裹位置参数使用一个"*"来表示,最后会被封装为元组,而包裹关键字参数使用"**"表示,最后会被封装为字典。

其语法格式为:

# 包裹参数def 函数(*参数):    代码    # 包裹关键字def 函数(**参数):    代码

下面给出对应的例子:

# 包裹位置参数def user_info(*args):    print(args)    print(f"args的类型为:{type(args)}")# 包裹关键字传递def user_info_key(**kwargs):    print(kwargs)    print(f"kwargs的类型为:{type(kwargs)}")    user_info("Tom")user_info("Tom", 18)user_info_key(name="Tom", age=18, gender="男")

执行结果如下:

五、拆包和交换变量值

5.1 拆包

所谓拆包就是将函数的返回值还原为多个参数,当然,拆包也分为:元组拆包和字典拆包。需要说明的是,拆包时,指定的变量数量必须与元组数量或字典的键值对数量保持一致,否则会报错。下面给出对应的示例:

# 对元组进行拆包def return_num():    return 100, 200# 将返回值进行拆包num1, num2 = return_num()print(f"num1:{num1}")print(f"num2:{num2}")# 对字典进行拆包dict1 = {"name": "Tom", "age": 18}a, b = dict1print(f"a: {a}, dict1[a]:{dict1[a]}")print(f"b: {b}, dict1[a]:{dict1[b]}")

执行结果如下:

5.2 变量值交换

Python提供了专门的多变量交换语法,下面为对应的语法格式:

变量a,变量b=变量b,变量a

对于Java而言,若需进行变量交换,则只能通过额外的临时变量实现。

下面为对应的例子:

a, b = 1, 2print(f"执行交换操作前a的值为:{a}, b的值为:{b}")a, b = b, aprint(f"执行交换操作后a的值为:{a}, b的值为:{b}")

执行结果如下:

六、递归

所谓递归就是函数在运行过程中会调用到函数本身。对于递归而言,其必须满足如下几个特点:

  1. 递归过程中会不断调用自己
  2. 递归过程需存在终止条件,且不断向着终止条件进行递归
  3. 递归过程需存在一个无需进行计算就能得出的初始值。

当然,对于大多数编程语言来说,都会提供递归操作,python自然也不例外。下面给出在Python中进行递归的例子:

def fibra(num):    if num <= 2:        return num    return fibra(num - 1) + fibra(num - 2)for i in range(1, 10):    print(f"当i为{i}时结果为:{fibra(i)}")

上述例子其实就是计算斐波那契数列的实现,当然,也常用于解决爬楼梯问题。其中,当num = 1时为终止条件,而num = 1或2时会直接指定初始值,也就是给出对应的斐波那契数值。当num > 2时的斐波那契数值需经过递归进行计算。

执行结果如下:

七、Lambda表达式

7.1 概述

与Java类似,Python中也存在Lambda表达式。所谓的Lambda表达式其实就是一种特殊的语法。在Java中,Lambda表达式只能应用于函数式接口。所谓函数式接口就是有且仅有一个抽象方法的接口。而在Python中则不一样,其将Lambda表达式应用于只有一句代码且只有一个返回值的函数,从这点来看,反而有点像Java中Lambda表达式的一种特例,也就是可用方法引用进行替代的场景。

下面为Python中Lambda表达式的语法格式:

lambda 参数列表:表达式

需要说明的是,lambda表达式中的参数可有可无,但是lambda表达式的参数必须与对应函数的参数保持一致。

下面给出一个例子:

# 普通函数def add(a, b):    return a + bm = 2n = 3print(f"m和n使用add函数计算结果为:{add(m, n)}")# 使用lambda表达式add2 = lambda a, b: a + bprint(f"m和n使用add2函数计算结果为:{add2(m, n)}")

执行结果如下:

7.2 常见形式

7.2.1 无参

所谓无参就是无需传入任何参数,下面给出对应的例子:

# 无参fn = lambda: print("hello world")fn()

执行结果如下:

7.2.2 一个参数

所谓一个参数就是只传入一个参数,下面给出对应的例子:

# 带一个参数fn = lambda text: print(text)fn("hello world")

执行结果如下:

7.2.2 默认参数

所谓默认参数是指为参数设置默认值,在调用时若未指定参数,则是用该默认值。下面给出对应的例子:

# 带默认参数fn = lambda a, b, oper = "*" : print(eval(f"{a} {oper} {b}"))# 传参时fn(1, 2, "+")# 不传参时fn(1,2)

执行结果如下:

7.2.4 可变参数*args

所谓可变参数是指传入的参数不定,需要说明的是,最后传入的参数会被封装为元组。下面给出对应的例子:

# 可变参数*argsfn = lambda *args: argsresult = fn("Tom", "Jerry", "Spike", "Tuffy", "Tyke")print(f"结果为:{result}")print(f"对应的类型为:{type(result)}")

执行结果如下:

7.2.5 可变参数 **kwargs

对于**kwargs可变参数而言,其与*args的区别在于,其会被封装为字典类型,下面给出对应的例子:

# 可变参数**kwargsfn = lambda **kwargs: kwargsresult = fn(name="Tom", age="20", sex="男")print(f"结果为:{result}")print(f"对应的类型为:{type(result)}")

执行结果如下:

7.3 使用场景

最常见的应该是列表按字典的key进行排序,下面给出对应的例子:

persons = [    {"name": "Tom", "age": 18, "sex": "男"},    {"name": "Jerry", "age": 17, "sex": "女"},    {"name": "Spike", "age": 22, "sex": "男"},    {"name": "Tuffy", "age": 2, "sex": "女"},    {"name": "Tyke", "age": 6, "sex": "男"}]# 按照name升序排序persons.sort(key=lambda p: p["name"])print("按照name升序排序后结果为:")for p in persons:    print(p)# 按照年龄降序排序persons.sort(key=lambda p: p["age"], reverse=True)print("按照age降序排序后结果为:")for p in persons:    print(p)

执行结果如下:

八、高阶函数使用

8.1 概述

所谓高阶函数就是将一个函数作为另一个函数的入参。其实所谓高阶函数有点类似函数式编程。下面给出对应的示例:

# 不使用高阶函数实现两个数的绝对值求和def add_num(a, b):    return abs(a) + abs(b)# 使用高阶函数实现两个数的绝对值求和def add_num_1(a, b, abs_func):    return abs_func(a) + abs_func(b)print(f"2和-3的绝对值之和为:{add_num(2, -3)}")print(f"2和-3的绝对值之和为:{add_num_1(2, -3, abs)}")

执行结果如下:

8.2 内置的高阶函数

对于Python而言,其提供了一些内置的函数,常见的有:map、reduce和filter。

8.2.1 map

map函数会接收两个参数,分别为:func和list。其用途是使用传入的函数来对list进行处理,并返回新的数据结构。若为Python2,则返回的是列表;若为Python3,则返回的是迭代器。

下面给出对应的使用例子:

list1 = [1, 2, 3, 4, 5]# 将队列中的元素统一平方def pow(x):    return x ** 2result = map(pow, list1)print(f"返回结果的类型为:{type(result)}")print(f"处理后的数据为:{list(result)}")

执行结果如下:

8.2.2 reduce

对于reduce来说,其传入的函数必须有两个参数,其用途是通过传入的参数对序列的元素做累积运算。下面为对应的示例代码:

list1 = [1, 2, 3, 4, 5]def multiply(a, b):    return a * bdef add(a, b):    return a + b# 累加运算addResult = functools.reduce(add, list1)print(f"累加的结果为:{addResult}")# 累积运算multiplyResult = functools.reduce(multiply, list1)print(f"累积的结果为:{multiplyResult}")

执行结果如下:

8.2.3 filter

filter函数的用途是使用传入的函数来对序列进行过滤,下面为对应的例子:

list1 = [i for i in range(1, 11)]def func(x):    return x & 1 == 0result = filter(func, list1)print(f"过滤后的结果为:{list(result)}")

执行结果如下: