一、函数简介
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}")
执行结果如下:
六、递归
所谓递归就是函数在运行过程中会调用到函数本身。对于递归而言,其必须满足如下几个特点:
当然,对于大多数编程语言来说,都会提供递归操作,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)}")
执行结果如下: