一、函数的介绍
当我们写一个程序,如果相同的功能需要重复100次。
在没有函数的情况下,我们需要复制粘贴100次。整个脚本文件就变得非常庞大。
如果需要修改这个功能,那我们也需要修改100次。而且容易出错。 so 我们引进了函数。
1.1 定义函数
一个函数最基本的组成部分:
关键字 def
函数名 函数名要反应其意义,就是命名规范
注释 代码规范,解释该函数的作用,以及参数的作用
函数体 这个函数需要完成的逻辑功能。
返回值 默认为None,可以不写。但是我们都会定义自己的返回值。注意:函数一遇到return 就会结束了,后面的代码就不会执行了(if else 嵌套return)。
def 函数名(参数1,参数2,参数3,...): #函数名要能反映其意义 '''注释''' 函数体 return 返回的值 #
1.2 函数的运行过程与调用
1.2.1 调用
函数名本质上就是函数的内存地址的标签
调用: 函数名()
第一步: 先找到函数名
第二步: 根据找到的函数名,运行其中保存的代码
函数名其实就是一个变量,其中保存了 函数的代码。 加上一个() 就运行了其中保存的代码了。
1.2.2 运行过程
1 x = 100
2 def add(a, b):
3 y = a + b
4 return y
5 su = add(x, 100)
第一步: 把 x = 100 加载到内存中。
第二步: 把函数名add 与其 对应的函数体代码 放在内存中。 注:其下的代码在这时候,是没有运行的,只是保存到变量中。
第三步:直接到第5行。 在内存中,找到上一步,add函数名变量。遇到(),执行该函数变量的保存的代码。这一步涉及到传递参数的内容,在下面会讲解。
第四步:遇到return,返回到第5行,给su 赋值。
1.3 函数的注释规范
def odd(a: int, b: '默认值'=3 ) -> '写返回值':
"函数说明,介绍函数的功能,会添加到__doc__中,可使用三引号多行,en首单词首字母大些"
return a + b
print(odd(3))
二、参数传递
1 x = 10000
2
3 def add(a, b): # 实参 到 形参的传递,其实就是指向了同一块内存,有两个别名而已
4 print('Before', id(a))
5 a += 1 # a = a + 1 ,前面的a是局部变量a(执行这句生成),新的一块内存, 后面的a是全局的
6 print('after', id(a))
7
8 print('globle before', id(x))
9 add(x, 2)
10 print('globle after', id(x))
三、 参数类型
函数中参数可以分为 位置参数 、 默认参数 、不定长参数
参数排序的规则: 位置参数-->*args-->默认参数-->**kwargs 否则,解释器进行语法分析的时候 直接报错。
3.1 位置参数-必须传递参数
def mymax(x, y):
the_max = x if x > y else y
return the_max
maxnum = mymax() # 此处不传参数
print(maxnum)
计算结果报错:
maxnum = mymax()
TypeError: mymax() missing 2 required positional arguments: 'x' and 'y'
3.2 默认参数
可以不传递参数,但是默认参数,只会计算一次(在第一次调用的时候,进行赋值),如果进行了赋值,则指向一块新的内存地址。
如下:
def func(a, b=[]): # b的默认值指向一个空的列表,每次不带默认值都会指向这块内存
print(id(b), end='---')
b.append(a)
return b
print(func(1)) # 向默认的空列表里加入元素1 ,默认列表里已经是[1]
print(func(2)) # 向默认的列表里加入元素2,默认列表里已经是[1,2]
print(func(3, [])) # 向b指向新的一块内存地址的空列表里加入元素1 ,默认列表里还是[1,2]
print(func(4)) # 向默认的列表里加入元素4,默认列表里已经是[1,2,4]
运行结果:
4352862280---[1]
4352862280---[1, 2]
4352860872---[3]
4352862280---[1, 2, 4]
3.3 关键字参数
关键字参数是针对 函数的调用来讲的。 使用关键字参数来调用函数,就可以不按照次序来传递。
def mymax(x, y):
# 此时x = 20,y = 10
the_max = x if x > y else y
return the_max
maxsum = mymax(y=10, x=20) # 关键字传参,不按照定义的次序
print(maxsum)
计算结果
20
3.4 不定长参数 *args
不定长参数 *args ,接受位置参数,在函数中会转换为一个元祖。
def func(*args): #这种定义会把传递的参数包成元组
print(args,type(args))
func(10,20)
#结果:
#(10, 20) <class 'tuple'>
举一个相反的例子:
def func(a,b):
print('a=%d, b=%d' % (a,b) )
a = (10, 20)
func(*a) # 在调用函数使用`*`则会把元组解包成单个变量按顺序传入函数
#结果:a=10, b=20
总结:*
号在定义函数参数时,传入函数的参数会转换成元组,如果 *
号在调用时则会把元组解包成单个元素(元祖的形式)。
3.5 不定长参数 **kwargs
def func(**kw):#使用**定义参数会把传入参数包装成字典dict
print(kw, type(kw) )
func(a=10,b=20)#这种函数在使用时必须指定参数值,使用key=value这种形式
#结果:{'b': 20, 'a': 10} <class 'dict'>
举一个相反的列子
def func(a,b):
print('a=%d, b=%d' % (a,b) )
d = {'a':10, 'b':20 }
func(**d) #在调用时使用**会把字典解包成变量传入函数。
总结:**
号在定义函数参数时,传入函数的参数会转换成字典,如果 **
号在调用时则会把字典解包成单个元素(k=v形式)。
3.6 指定必须要传递一个参数
def car(a=1, *, price): # * 代表后面的参数必须要传递
print('waht')
car(price='haha') # 必须使用关键字参数,否则报错
四、lambda函数
一些函数功能比较简单,而我们不想去完整的去定义一个完整的参数,不想写函数名。
就可以去用lambda函数。又叫做 匿名函数。
def plus(x):
return x + 1
plus(19)
# 进行函数的改写
# 但是这种方法官方不建议,给匿名函数都命名了,还叫命名吗。。
add = lambda x: x+1
add(19)
# 或写成这种
print((lambda x: x + 1)(3))
扩展:filter + lambda
my_list = [1, 2, 3, 4, 5, 6]
def odd(*L):
new_list = []
for i in L:
if i % 1 == 0:
new_list.append(i)
return new_list
print(*my_list) # *把列表解开变成位置参数。
# 改装
'''
filter(func,iterable)
两个参数:
第一个参数 func :在这里写 函数就会显得冗长,那么我们就用lambda来写
第二个参数 iterbale :一个可迭代对象。
把迭代对象中每个元素拿到func中去执行,满足就留下来。
'''
# 第一步
lambda x: x % 2 == 0
# 第二步 返回的是一个可迭代的对象
filter(lambda x: x % 2 == 0, my_list)
# 第3步 转换为列表
y = list(filter(lambda x: x % 2 == 0, my_list))
print(y)