一、语法格式:
def test(x):
"解释函数的作用"
x += 1
return x
def:定义函数的关键字
test:函数名
():定义函数的形参
"":文档描述,定义函数的作用
x+=1:函数的代码块
return:定义返回值
当函数遇到一个return时整个函数结束
当指定了默认参数的时候,在调用函数的时候可以不指定参数
二、函数的参数:
1、形参:
在函数中定义的变量,其只有在被调用的时候才会被分配内存单元,在调用结束时释放分配的内存单元
2、实参:
在调研用函数的时候定义的常量、变量、表达式、函数等
3、例子:
def add(x,y):
res = x * y
return res
add = add(2,4)
在上面的例子中形参为add后面的x和y,实参为调用函数时的2和4;形参和实参的位置必须要一一对应
三、可变长参数:
可变长参数是指可以向函数的传递不定个数的参数;可变长参数的原则是位置参数必须在关键字参数之前
1、位置参数:
位置参数是和函数中的形参一一对应的,如:
def add(x,y):
res = x * y
return res
add(2,4)
在上面的代码中2和4对应函数中x和y,也就是x和y是位置参数
2、关键字参数:
关键字参数是指可以接受0个或多个实参的参数,如:
def test01(x, *args):
print(x)
print(args)
test01(1,2,3,4,5)
在上面的代码中x是位置参数,对应实参中的1;
而*args是一个关键字参数,对应实参中的2,3,4,5
3、*:
在函数的形参中*可接受字符串、数字、列表、元组、集合的数据类型;Python最后会将这些数据类型转化为元组。
例子:
def test01(x, *args):
print(x)
print(args)
test01(1,2,3,4,5)
运行结果:
1
(2, 3, 4, 5)
若传递的实参是一个可迭代的参数时需要加上一个*号,如:
def test01(x, *args):
print(x)
print(args)
test01(1, *[2,3,4])
运行结果:
1
(2, 3, 4)
4、**:
和*类似,不同的是**可接收的是键值对,最后都会转换为字典形式
例子:
def test02(x, **kwargs):
print(x)
print(kwargs)
test02(1, y=2, z=3)
运行结果:
1
{'y': 2, 'z': 3}
和*类似**也可以接收字典类型,如:
例子:
def test02(x, **kwargs):
print(x)
print(kwargs)
test02(1, **{"y":2, "z":3})
运行结果:
1
{'y': 2, 'z': 3}
5、当 * 和 ** 共用时:
因为可变长参数的原则是位置参数必须在关键字参数之前,所以当*和**共用时为了避免产生歧义,需要将*放在**之前;如:
def test03(x, *args, **kwargs):
print(x)
print(args)
print(kwargs)
test03(1,2,3,4,y=1,z=2)
运行结果:
1
(2, 3, 4)
{'y': 1, 'z': 2}
四、全局变量和局部变量
1、全局变量的定义:
在Python代码中的任意位置都能调用的变量;如:
name = "xiaoming"
def get_name():
print(name)
get_name()
运行结果:
xiaoming
在上面的例子中在函数外定义了一个变量 “name”,然后在函数内部调用这个变量;最后发现我们可以调用“name”变量;所以变量“name”被称为全局变量
2、局部变量的定义:
在Python代码中只在特定过程或函数中可以访问的变量,当没有在子程序中定义局部变量时会去上一层进行查找是否存在相同的变量名;如:
name = "xiaoming"
def get_name1():
name = "xiaohong"
print(name)
def get_name2():
print(name)
get_name1()
get_name2()
运行结果:
xiaohong
xiaoming
根据上面的例子可以发现先定义的全局变量在函数“get_name1”中并没有被调用,而是调用了函数内部的定义的“name”变量;而函数“get_name2”调用了全局变量“name”,无法调用函数“get_name1”中定义的“name”变量;所以在函数“get_name1”中的变量“name”称之为局部变量。
3、global关键字:
在函数中的局部变量之前使用 global 关键字可以将局部变量定义为全局变量;如:
name = "xiaoming"
def get_name1():
global name
name = "xiaohong"
print(name)
def get_name2():
print(name)
get_name1()
get_name2()
运行结果:
xiaohong
xiaohong
在上面的例子中,在函数“get_name1”中使用了关键字 global 将函数“get_name1”中的局部变量“name”转化成了全局变量“name”;因为在代码的开头已经定义了全局变量‘name=“xiaoming”’,所以在函数“get_name1”中的变量“name”将代码开头的全局变量“name”的值改成了“xiaohong”,所以在函数“get_name2”中调用“name”的值成了“xiaohong”。
4、函数的嵌套
函数支持多个函数进行嵌套,如:
name = "xiaoming"
def get_name1():
name = "xiaobai"
print(name)
def get_name2():
global name
name = "xiaohong"
get_name2()
get_name1()
print(name)
运行结果:
xiaobai
xiaohong
注:函数在没有被调用的情况下只进行编译,不执行。
当在调用函数“get_name1()”的时候局部变量“name”的值是“xiaobai”,所以在函数“get_name1()”中print的时候输出“xiaobai”;在函数“get_name1()”中调用了函数“get_name2()”,在函数“get_name2()”中使用了global关键字将局部变量name变为了全局变量name并进行了修改,所以在代码最后print的时候输出“xiaohong”
5、nonlocal关键字
nonlocal关键字和global关键字类似,不同的是global关键字是将局部变量转换为全局变量;而nonlocal关键字是将局部变量转为上一级函数的局部变量,当函数只有一级的时候使用nonlocal程序会报错,如:
name = "xiaoming"
def get_name1():
name = "xiaobai"
print(name)
def get_name2():
nonlocal name
name = "xiaohong"
get_name2()
print(name)
get_name1()
print(name)
运行结果:
xiaobai
xiaohong
xiaoming
6、作用域
函数的作用域和函数的定义有关,和函数的调用位置无关;如:
name = "xiaohong"
def name1():
name = "xiaoming"
def name2():
print(name)
return name2
name1()()
运行结果:
xiaoming
解释:
当调用函数name1的时候程序会返回函数name2的内存地址,所以name1()()即时调用函数name2,因为name2是在name1下嵌套的一个函数,而name2本身没有局部变量name,所以程序会到name2的上一层函数中寻找是否有变量name,有即输出,若没有找到则去全局变量中找。
五、向前引用(风湿理论)
下面通过四种情况来理解向前引用
在Python中函数即变量!
情况1:
name = "xiaoming"
def get_name1():
print(name)
get_name2()
get_name1()
运行结果:
报错
解释:
当创建一个函数的时候Python会在内存中获取一个空间,用该空间来存放函数内的代码块,并将这个内存空间指向函数名,就和变量一样;在情况1中指向的是get_name1。因为在get_name1中的代码块中又调用了一个函数get_name2,但在内存中没有存在指向get_name2的内存空间,所以Python无法执行这行代码,固报错。
情况2:
name1 = "xiaoming"
name2 = "xiaohong"
def get_name1():
print(name1)
get_name2()
def get_name2():
print(name2)
get_name1()
运行结果:
xiaoming
xiaohong
解释:
和情况1类似,不同的是创建了一个函数“get_name2”,即内存中有内存空间指向了get_name2,所以在get_name1中调用get_name2的时候Python能够执行相应的内容
情况3:
name1 = "xiaoming"
name2 = "xiaohong"
def get_name2():
print(name2)
def get_name1():
print(name1)
get_name2()
get_name1()
运行结果:
xiaoming
xiaohong
解释:
和情况2一样,内存中存在执行get_name1和get_name2的内存空间,所以get_name1和get_name2都能被调用并执行。
情况4:
name1 = "xiaoming"
name2 = "xiaohong"
def get_name1():
print(name1)
get_name2()
get_name1()
def get_name2():
print(name2)
运行结果:
报错
解释:
因为Python代码是从上往下执行的,所以当我们在代码中间调用get_name1的时候Python会去内存中找是否存在指向get_name1的内存空间;可以发现能够在内存中找到指向get_name1的内存空间,然后执行其中的get_name1的代码块,发现其中调用了函数get_name2,但是get_name2函数在这时候还没有被创建,内存中不存在指向get_name2的内存空间,所以无法执行。
六、递归函数
1、递归函数的定义
递归函数是指在函数的内部调用自己
2、特性
- 必须有一个明确的结束条件
- 每次执行一次递归后问题的规模都应比上一次减少
- 递归的效率不高,当递归的次数较多的时候会占用内存
3、案例:
1、使用递归函数计算1到100之间的和
def sum(n):
if int(n) == 1:
return n
else:
return sum(n - 1) + n
print(sum(100))
运行结果:
5050
2、使用递归函数计算10的阶乘
def product(n):
if n == 1:
return 1
else:
return product(n - 1) * n
print(product(10))
运行结果:
3628800
4、尾递归调用的优化
定义:在函数执行完最后一步的时候在去调用其它函数。(注意:是最后一步而不是最后一行)
在递归函数的特性中已经知道了递归函数会占用内存,原因是在函数中调用函数时若当前函数并没有完全执行完就调用了函数就会造成内存空间的占用,如:
def func1(x):
return x
def func2(x):
return func1(x) + 1
print(func2(4))
解释:
函数func2的return值就相当于两步:1、res = func1(x) 2、return res + 1;在执行第一步的时候函数func2会等待func1执行完后返回的值,所以递归函数若用非尾调用的方法写会作用内存空间
将上面的代码改为尾调用
def func1(x):
return x + 1
def func2(x):
return func1(x)
print(func2(4))
七、匿名函数
和def定义的函数不同的是匿名函数是一个表达式,定义方法如下:
lambda x:x+1
这相当于def函数中的
def test(x):
return x+1
例子:
1、生存一个列表,要求元素必须是2的1-10次方,如:[2,4,8,16,...]
list01 = []
func = lambda x:2**x
for i in range(1,11):
list01.append(func(i))
print(list01)
运行结果:
[2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
2、修改上面的代码将列表内的所有元素都乘以100后输出
list01 = []
func = lambda x:2**x*100
for i in range(1,11):
list01.append(func(i))
print(list01)
运行结果:
[200, 400, 800, 1600, 3200, 6400, 12800, 25600, 51200, 102400]