目录
函数是Python中重要的组成部分,它可以将代码封装起来,复用代码,可以很好的简化代码结构。
函数定义格式:
函数的内存分析:函数也是对象
内存分为栈内存和堆内存,函数在定义时,栈内存中存储的是函数的地址,用来找到堆内存中的函数对象,既然函数是对象,那么函数肯定具有函数类的相关属性和方法,比如函数使用的局部变量、返回值、内部__doc__方法等。
def test01():
"""
我是测试函数
"""
print("*"*10)
test01()
c = test01
c()
print(id(test01))
print(id(c))
print(type(test01))
print(type(c))
print(test01)
print(c)
函数入口 :向函数传递参数
函数参数有多种类型:实参、形参、可变参数、不可变参数、位置参数、默认参数、命名参数、可变参数、强制命名参数等。
1.形参和实参:
将函数外部拥有具体值的变量(有实际意义的参数)传入函数内部(函数括号中的参数叫做形参,形式上的参数)。
2.可变参数和不可变参数:
当传递形参时,形参按照类型分,又分为可变参数和不可变参数;因此向函数传递参数时,又要分可变参数和不可变参数。
可变参数,就是可以在原数据上改变的参数,比如(列表,字典)可以在已经定义的列表上进行增删改等操作。如果传入的实参是可变参数,实际上它传递的是实参对象的引用,在函数体中不创建新的对象拷贝,形参直接指向实参对象的引用,对外部实参进行修改。
不可变参数,只要是定义好的数据,就不可以在原数据上进行修改,比如(int,float,字符串,元组,布尔值)不可以在原数据上进行增删改等操作。如果传入的实参是不可变参数,实际上它传递的也是实参对象的引用,由于不可变的特性,会在函数体中创建新的对象,此时不会指向外部的实参对象,也就不会对外部实参进行修改。
传递可变参数:
b = [10,20]
def f2(m):
print("m",id(m)) # b和m是同一个对象,id相同
m.append(30) # 由于m是可变对象,不创建对象拷贝,直接修改这个对象
f2(b)
print("b:",id(b))
print(b) # 在函数内部改变了b,因此b=[10,20,30]
传递不可变参数:
a = 100
def f1(n):
print("n:",id(n)) # 传递a的地址
n = n+200 # 由于a是不可变对象,因此创建新的对象n
print("n:",id(n)) # n已经变成了新的对象,和a的id不同
print(n)
f1(a)
print("a:",id(a))
传递可变参数内存分析:
传递不可变参数内存分析:
传递可变参数和不可变参数其实涉及到深拷贝和浅拷贝知识点,其它博客有总结过相关知识点。
3.位置参数:
函数调用时,实参默认按位置顺序传递,需要个数和形参匹配,就是形参有几个就要传递几个实参,当然有默认参数的除外。
4.默认值参数:
为某些形参设置默认值,这样的参数在传递时是可选的。
5.命名参数:
如果不想像位置参数那样按照指定位置赋值,可以使用函数形参名称赋值,不用考虑顺序。
6.可变参数:
可变参数,可以理解为只有一个形参却可以接收任意数量的形参,将接收实参存储在元组或字典中。
1.*args(一个星号):将多个参数收集到一个“元组”对象中。
2.**kwgs(两个星号):将多个参数收集到一个“字典”对象中。
def f1(a,b,*c):
print(a,b,c)
f1(1,2,3,4,6,7)
def f2(a,b,**c):
print(a,b,c)
f2(8,9,name="cwj",age=18)
def f3(a,b,*c,**d):
print(a,b,c,d)
f3(8,9,10,11,12,name="lzy",age=18)
7.强制命名参数:
指定形参名赋值,在带星号的“可变参数”后面增加新的参数,必须在调用的时候强制“命名参数”。
def f1(*a,b,c):
print(a,b,c)
# f1(2,3,4) # 会报错。由于a是可变参数,将2,3,4全部收集,造成b和c无数据
f1(2,b=3,c=4)
函数内部:对传入的参数进行逻辑处理。
当函数内部使用变量时,分为使用函数内部的变量还是函数外部的变量,内部的叫做局部变量,外部的叫做全局变量。
全局变量和局部变量:
全局变量和局部变量的主要区别在于变量的作用域不同,全局变量是在整个程序中,每个地方都可以访问到的变量,局部变量是只有在定义该变量的函数内部才可以访问的变量。
a = 100 # 全局变量
def test01():
a = 300 #局部变量
print("我是局部变量a={0}".format(a)) # a = 300
def test02():
global a # 如果想要访问全局变量a,需要使用global
print("我是全局变量a={0}".format(a)) # a = 100
def test03():
global a # 设置全局变量,先global,再赋值
a = 400
print("我是全局变量a={0}".format(a)) # a = 400
test01() # 局部变量和全局变量重名,优先考虑局部变量
print(a) # 查看a是否被改变了
test02() # 访问全局变量
print(a) # 查看a是否被改变了
栈帧(stack frame):
简单理解就是函数局部变量创建时,生成栈帧用来存放局部变量,函数访问完毕,栈帧销毁,再次调用函数,再次创建栈帧。
函数出口:return语句
lambda表达式:
lambda表达式可以用来声明匿名函数,它是一种简单的定义函数的方式,只允许包含一个表达式,不能包含复杂语句。
lambda表达式的计算结果就是函数的返回值。
lambda表达式实际生成的是一个函数对象。
lambda表达式的基本语法如下:
f = lambda a,b,c:a+b+c
print(f)
print(f(2,3,4)) # 9
g = [lambda a:a*2,lambda b:b*3, lambda c:c*4]
print(g[0](2),g[1](3),g[2](4)) # 4 9 16
递归函数:
递归函数简单的讲就是函数自己调用自己。在写递归函数的时候,一定要注意函数的终止条件,否则会造成栈溢出。
每个递归函数必须包括的部分:
1、终止条件:递归什么时候结束,一般使用return 返回。
2、递归步骤:把第n步和第n-1步相关联。
递归的缺点:由于递归函数会创建大量的函数对象,过量的消耗内存和运算能力,不适合处理涉及到很多数据的问题。
def fib(n):
if n==1: # 1.终止条件
return 1
else:
return n*fib(n-1) # 2.n和n-1相关联
print("5的阶乘:",fib(5))
嵌套函数:
在函数内部定义的函数,叫做嵌套函数。
def outer():
print("outer running")
def inner():
print("inner running")
inner()
outer()
nonlocal和global:
在使用嵌套函数的时候,如果内部函数想要使用外部函数的变量,需要借助关键字nonlocal来声明外部变量。
def outer():
b = 10
def inner():
nonlocal b # 声明外部变量b
print("inner b:",b) # 如果直接读取b的值,不需要使用nonlocal
b = 20 # 如果没有使用nonlocal声明, 会报错UnboundLocalError
inner()
print("outer b:",b)
outer()
在使用嵌套函数的时候,如果内部函数想要使用全局变量,需要借助关键字global来声明全局变量。
a = 888
def outer():
b = 10
global a
a = 999
print("outer--->a",a)
def inner():
nonlocal b
print("inner b:",b) # 如果直接读取b的值,不需要使用nonlocal
b = 20 # 如果没有使用nonlocal声明, 会报错UnboundLocalError
global a
a = 666
print("inner--->a:",a)
inner()
print("outer b:",b)
print("a:",a)
outer()
print("a:",a)
LEGB规则:
LEGB:函数查找同名变量的顺序,L --> E --> G --> B
str = "level 3" # 如果注释掉,会打印 <class 'str'> (Global) --> Bulid in
def outer():
str = "level 2" # 如果注释掉,会打印 level 3 (Enclosed) --> (Global)
def inner():
str = "level 1" # 如果注释掉,会打印 level 2 (Local) --> (Enclosed)
print(str)
inner()
outer()
函数其它小用法:
函数.doc:获取函数多行字符串中的描述信息。
def test01():
"""
我是测试函数
"""
print("*"*10)
print(test01.__doc__)
会输出:我是测试函数
获取函数内部的局部变量和使用到的外部全局变量的方法:locals()和globals()
a = 100
def f1(a,b,c):
print(a,b,c)
print(locals()) # 打印输出局部变量
print(globals()) # 打印输出全局变量
f1(2,3,4)