第六天
概述:具有名称的能够完成某一特定功能的代码的集合;为了模块化的管理,即将不同功能的代码进行分类,个模块化之间要遵循高内聚,低耦合;
python中函数的定义
def function_name([args...]):
函数体
功能的集合
[return 返回值] 可选
python中函数的调用;注意:调用函数与定义函数间空两行(潜规则)
function_name()
6.1 函数分类
定义有没有参数
无参函数
有参函数
函数的参数,要根据实际来决定,是否需要参数
定义是否有返回值
无返回值
有返回值
在函数中,当遇到return关键字的时候,函数会立刻返回
6.2 全局/局部变量
全局变量:定义在模块(.py)中,可以在该模块定义后任何地方都可以访问;从程序的开始到结束都是可以被执行的变量
局部变量:定义在函数内部的变量,作用域只是在当前函数内有效,出了函数,无法访问到
在函数中,能不能访问到全局变量?
当然可以,但是注意:在python中,函数内不允许修改全局变量
如果确实在函数内,需要修改全局变量,必须要提前申明
global 全局变量
例1:
name = "Eichi"
def print_best():
name = "Owen"
print(name)
print_best()
print(name)
执行结果:
Owen
Eichi
总结:把握就近原则,即在函数内部,会选择最近的局部变量name作为输出,别看两个变量的名称相同,其存储在执行栈的不同位置,下面会做详细的解释
例2:
name = "Eichi"
def print_best():
name += "Owen"
print(name)
print_best()
print(name)
执行后就会出错,如下,意思是没有绑定的本地变量错误:赋值前引用了局部变量“name”
Traceback (most recent call last):
File "Class6全局局部变量.py", line 8, in <module>
print_best()
File "Class6全局局部变量.py", line 5, in print_best
name += "Owen"
UnboundLocalError: local variable 'name' referenced before assignment
解决:在函数中对全局变量进行声明,这样就可以在函数内部进行修改了
name = "Eichi"
def print_best():
global name
name += "Owen"
print(name)
print_best()
print(name)
执行结果:
EichiOwen
EichiOwen
总结:要想在函数中修改全局变量的值就要对全局变量进行声明
6.3 函数的调用
压栈,函数调用本质就是将堆中的函数调用到栈中执行,这个过程被称为压栈,等函数执行完成,会立刻执行出栈(弹栈)
总结:
- 定义的函数是在堆中存储的
- 调用函数时,将堆中的存储的内容,放到执行栈中;此过程被称为压栈
- 等函数执行完成后,弹出栈,所以函数中的局部变量就被弹出去了,即不能访问,而全局变量从一开始就被压入执行栈中,所以何时都可以进行使用
- ·此时就可以解释为什么在函数中不能修改全局变量的值了,全局变量存储在执行栈的函数下面,且随着程序的执行一直存储,而函数执行完成后就会被销毁,所以说不能修改,至于就近原则的产生是因为栈的存储结构决定的,即先进后出
6.4 参数
6.4.1 参数传递
- 值传递:本质就是传递数据值的拷贝,跟原先的那个值没有关系;即弹栈后并不会修改全局变量的值
- 引用传递:本质是传递指针(地址),所以说实质操作的就是堆,所以局部变量被回收后,由于堆空间的存在,还是可以使用的
例1:值传递
def change(x,y):
x = 2 * x
y = 3 * y
print(x)
print(y)
num1 = 10
num2 = 20
change(num1,num2)
print(num1)
print(num2)
执行结果:
20
60
10
20
例2:引用传递
def add_user(users):
users.append("haha")
users.append("hehe")
#ls本质上存储在堆内存中,所以执行栈中放的是地址
if __name__ == "__main__":
ls = ["Eichi","Tong","1chen"]
add_user(ls)
print(ls)
执行结果:
['Eichi', 'Tong', '1chen', 'haha', 'hehe']
另外说明:
Python中的main函数主要作用:写在main函数的代码并不会被导入到其他模块中
注意:Python中的函数运行返回多个值,以元组形式进行返回
举例:
def change(x,y):
x = 2 * x
y = 3 * y
return x, y
if __name__ == "__main__":
num1 = 10
num2 = 20
res = change(num1,num2)
print(res)
执行结果:
(20, 60)
6.4.2 参数分类
-
默认值参数
默认值参数,在定义函数时,给定一个最常用的值,在调用的时候,如果有实参,那就用实参,如果没有,就使用默认的
注意:默认值参数,必须放在普通后面!!!
例1:
def get_cricle_area(r, pi=3.14):
return r * r * pi
print(get_cricle_area(3))
print(get_cricle_area(3,3.14))
print(get_cricle_area(3,3.1415))
print(get_cricle_area(3,3.1415926))
执行结果:
28.26
28.26
28.273500000000002
28.2743334
-
可变参数
顾名思义,参数是根据调用的实参的数量决定的
注意:args以元组的形式保存
例2:
def show(name, age, *args):
print(name,age,*args)
return args
res = show("Eichi",16,"love","viva","la","vida")
print(res)
执行结果:
Eichi 16 love viva la vida
('love', 'viva', 'la', 'vida')
- 命名参数(关键字参数),即keyword参数
注意:kwargs是以字典的形式保存
例3:
def show2(name, age, **kwargs):
print(name)
print(age)
print(kwargs["address"])
print(kwargs.get("height"))
return kwargs
if __name__ == '__main__':
kw = show2("Eichi", 20, gender="男", address="earth", height=1.90)
print(kw)
执行结果:
Eichi
20
earth
1.9
{'gender': '男', 'address': 'earth', 'height': 1.9}
组合之万能参数,即扩展性极强
def show3(name, age, gender, *args, **kwargs):
pass
6.5 函数递归
函数自身调用自身
注意:必须要有终止条件,没有终止条件的递归是一个死循环,直到栈溢出为止
栈溢出:StackOverflowException
Python跑在操作系统中的
Java跑在虚拟机里的
栈空间:比较小的一块,只牵扯到地址的引用,但是递归时整个栈空间被疯狂的使用占据,如下图所示,若函数不停的调用自身,且没有中止条件,则会造成执行栈的空间溢出
注意:函数的递归在Python中是由限制的,默认不能超过1000次,此默认值可以修改
import sys
# python中规定递归默认不能超过1000次
print(sys.getrecursionlimit())
# 修改递归的默认极限值
sys.setrecursionlimit(10000)
def get_sum(n):
# 递归必须要有终止条件,如果没有终止条件,递归就是一个死循环
# 直到栈溢出为止
if n == 1:
return 1
return n + get_sum(n - 1)
res = get_sum(100)
print(res)
执行结果:
1000
5050
import sys
# python中规定递归默认不能超过1000次
print(sys.getrecursionlimit())
# 修改递归的默认极限值
sys.setrecursionlimit(10000)
def get_sum(n):
# 递归必须要有终止条件,如果没有终止条件,递归就是一个死循环
# 直到栈溢出为止
if n == 1:
return 1
return n + get_sum(n - 1)
res = get_sum(100)
print(res)
执行结果:
1000
5050
今天的内容到这里就结束了~ 需要做题巩固练习的,请保持关注,持续更新中ing