学习目标:
1、了解函数的概念及优势。
2、掌握函数的定义和使用、函数参数的传递。
3、理解变量作用域。
4、掌握局部变量和全局变量、递归函数和匿名函数。
6.1函数概述
函数是组织好的、实现单一功能或相关联功能的代码段。我们可以将函数视为一段有名字的代码,这类代码可以在需要的地方以“函数名()”的形式调用。
print() 输出
input() 输入
函数式编程具有以下优点:
1)将程序模块化,既减少了冗余代码,又让程序结构更为清晰
2)提高开发人员的编程效率
3)方便后期的维护与扩展
6.2.1定义函数
前面使用的print()函数和input()都是Python的内置函数,这些函数由Python定义。开发人员也可以根据自己的需求定义函数,Python中使用关键字def来定义函数,
无参函数
def add():
result = 11 + 22
print(result)
有参函数
def add_modify(a, b):
result = a + b
print(result)
6.2.2 调用函数
函数在定义时可以在其内部嵌套定义另外一个函数,此时嵌套的函数称为外层函数,被嵌套的函数称为内层函数。
例子:
def add_modify(a, b):
result = a + b
add()
print(result)
add_modify(10, 20)
注意:
函数外部无法直接调用内层函数 只能通过外层函数间接调用内层函数
6函数参数的传递.3
我们通常将定义函数时设置的参数称为形式参数(简称为形参),将调用函数时传入的参数称为实际参数(简称为实参)。函数的参数传递是指将实际参数传递给形式参数的过程。
函数参数传递的分类:位置参数传递、关键字参数传递、默认参数传递、参数的打包与解包以及混合传递。
6.3.1位置参数的传递
函数在被调用时会将实参按照相应的位置依次传递给形参,也就是说将第一个实参传递给第一个形参,将第二个实参传递给第二个形参,以此类推。
def get_max(a, b):
if a > b:
print(a,"是较大的值!")
else:
print(b,"是较大的值!")
get_max(8, 5)
6.3.2 关键字参数的传递
关键字参数的传递是通过“形参=实参”的格式将实参与形参相关联,将实参按照相应的关键字传递给形参。
def connect(ip, port):
print(f"设备{ip}:{port}连接!")
connect(ip="127.0.0.1", port=8080)
Python在3.8版本中新增了仅限位置形参的语法,使用符号“/”来限定部分形参只接收采用位置传递方式的实参。
def func(a, b, /, c):
print(a, b, c)
# 错误的调用方式
# func(a=10, 20, 30)
# func(10, b=20, 30)
# 正确的调用方式 func(10, 20, c=30)
6.3.3 默认参数的传递
函数在定义时可以指定形参的默认值,如此在被调用时可以是否给带有默认选择值的形参传值,若没有给带有默认值的形参传值,则直接使用该形参的默认值。
定义:
def connect(ip, port=8080):
print(f"设备{ip}:{port}连接!"
调用
connect(ip="127.0.0.1")
connect(ip="127.0.0.1", port=3306)
结果
设备127.0.0.1:8080连接!
设备127.0.0.1:3306连接!
6.3.4 参数的打包与解包
如果函数在定义时无法确定需要接收多少个数据,那么可以在定义函数时为形参添加“*”或“**”:
“*” —— 接收以元组形式打包的多个值
“**”—— 接收以字典形式打包的多个值
打包“*”
def test(*args): print(args)
test(11, 22, 33, 44, 55)
(11, 22, 33, 44, 55)
打包“**”
def test(*args):
print(args)
test(11, 22, 33, 44, 55)
(11, 22, 33, 44, 55)
6.3.4 参数的打包与解包
1.打包——“**”
def test(**kwargs):
print(kwargs)
test(a=11, b=22, c=33, d=44, e=55)
{'a': 11, 'b': 22, 'c': 33, 'd': 44, 'e': 55}
1.打包——“**”
虽然函数中添加“*”或“**”的形参可以是符合命名规范的任意名称,但这里建议使用*args和**kwargs。 若函数没有接收到任何数据,参数*args和**kwargs为空,即它们为空元组或空字典。
6.3.4 参数的打包与解包
2.解包
实参是元组 → 可以使用“*”拆分成多个值 → 按位置参数传给形参
实参是字典 → 可以使用“**” 拆分成多个键
值对 → 按关键字参数传给形参
def test(a, b, c, d, e):
print(a, b, c, d, e)
nums = (11, 22, 33, 44, 55)
test(*nums)
{'a': 11, 'b': 22, 'c': 311 22 33 44 553, 'd': 44, 'e': 55}
解包
def test(a, b, c, d, e):
print(a, b, c, d, e)
nums = {"a":11, "b":22, "c":33, "d":44, "e":55}
test(**nums)
11 22 33 44 55
6.3.5 混合传递
前面介绍的参数传递的方式在定义函数或调用函数时可以混合使用,但是需要遵循一定的规则,具体规则如下。
优先按位置参数传递的方式。
然后按关键字参数传递的方式。
之后按默认参数传递的方式。
最后按打包传递的方式。
在定义函数时:
带有默认值的参数必须位于普通参数之后。
带有“*”标识的参数必须位于带有默认值的参数之后。
带有“**”标识的参数必须位于带有“*”标识的参数之后。
def test(a, b, c=33, *args, **kwargs):
print(a, b, c, args, kwargs)
test(1, 2) test(1, 2, 3)
test(1, 2, 3, 4)
test(1, 2, 3, 4, e=5)
1 2 33 () {} 1 2 3 () {} 1 2 3 (4,) {} 1 2 3 (4,) {'e': 5}
6.4 函数的返回值
函数中的return语句会在函数结束时将数据返回给程序,同时让程序回到函数被调用的位置继续执行。
def filter_sensitive_words(words):
if "山寨" in words:
new_words = words.replace("山寨", "**")
return new_words
result = filter_sensitive_words("这个手机是山寨版吧!") print(result)
结果
这个手机是**版吧!
#如果函数使用return语句返回了多个值,那么这些值将被保存到元组中。
def move(x, y, step):
nx = x + step
ny = y - step
return nx, ny # 使用return语句返回多个值
result = move(100, 100, 60)
print(result)
结果
(160, 40)
6.5 变量作用域
变量并非在程序的任意位置都可以被访问,其访问权限取决于变量定义的位置,其所处的有效范围称为变量的作用域。
6.5.1 局部变量和全局变量
根据作用域的不同,变量可以划分为局部变量和全局变量。
函数内部定义的变量,只能在函数内部被使用
函数执行结束之后局部变量会被释放,此时无法再进行访问。
def test_one(): number = 10 # 局部变量
print(number) # 函数内部访问局部变量
test_one() print(number) # 函数外部访问局部变量
#不同函数内部可以包含同名的局部变量,这些局部变量的关系类似于不同目录下同名文件的关系,它们相互独立,互不影响。
def test_one():
number = 10
print(number) # 访问test_one()函数的局部变量
number def test_two():
number = 20
print(number) # 访问test_two()函数的局部变量
number test_one() test_two()
结果:
10
20
6.5.1 局部变量和全局变量
全局变量可以在整个程序的范围内起作用,它不会受到函数范围的影响。
number = 10 # 全局变量
def test_one():
print(number) # 函数内部访问全局变量
test_one() print(number) # 函数外部访问全局变量
结果:
10
10
#全局变量在函数内部只能被访问,而无法直接修改。
# 定义全局变量
number = 10 def test_one():
print(number)
number += 1
test_one() print(number)
6.5.1 局部变量和全局变量
这是因为函数内部的变量number视为局部变量,而在执行“number+=1”这行代码之前并未声明过局部变量number。
#函数内部只能访问全局变量,而无法直接修改全局变量
6.5.2 global和nonlocal关键字
函数内部无法直接修改全局变量或在嵌套函数的外层函数声明的变量,但可以使用global或nonlocal关键字修饰变量以间接修改以上变量。
使用global关键字可以将局部变量声明为全局变量,
number = 10 # 定义全局变量
def test_one():
global number # 使用global声明变量number为全局变量
number += 1
print(number) test_one() print(number)
例:
def test():
number = 10
def test_in():
nonlocal number
number = 20
test_in()
print(number) test()
6.7.1 递归函数
函数在定义时可以直接或间接地调用其他函数。
若函数内调用部了自身,则这个函数被称为递归函数
递归函数在定义时需要满足两个基本条件:一个是递归公式,另一个是边界条件。其中: 递归公式是求解原问题或相似的子问题的结构; 边界条件是最小化的子问题,也是递归终止的条件。
递归函数的执行可以分为以下两个阶段:
1.递推:递归本次的执行都基于上一次的运算结果。
2.回溯:遇到终止条件时,则沿着递推往回一级一级地把值返回来。
格式
def函数名([参数列表]):
if 边界条件:
rerun 结果
else:
return 递归公式
例子
def func(num):
if num == 1:
return 1
else:
return num * func(num - 1)
num = int(input("请输入一个整数:"))
result = func(num)
6.7.2 匿名函数
定义好的匿名函数不能直接使用,最好使用一个变量保存它,以便后期可以随时使用这个函数。
# 定义匿名函数,并将它返回的函数对象赋值给变量
temp temp = lambda x :
pow(x, 2)
temp(10)
例题:
兔子一般在出生两个月之后就有了繁殖能力,每对兔子每月可以繁殖一对小兔子,假如所有的兔子都不会死,试问一年以后一共有多少对兔子? 本实例要求编写代码,利用递归实现根据月份计算兔子总数量的功能。
public class Fibonacci {
public static void main(String[] args) {
int f = f(10);
System.out.println(f);
}
public static int f(int n) {
if (n == 1) {
return 1;
} else if (n == 2) {
return 1;
} else {
return f(n - 1) + f(n - 2);
}
}
}
例题:
from functools import reduce
print(reduce(lambda x,y:x+y,[1,2,3,4,5])) #求1到5的数字和
print(list(map(lambda x:x ** 2,range(5)))) #求0到5各数的平方
运行结果:
15
[0, 1, 4, 9, 16]
print(reduce(lambda x,y:x*y,range(1,11)))
运行结果:3628800