1、定义
- 函数存在的现实意义:
在一个完整的项目中,某些功能可能会被反复使用,如果将反复出现的代码封装成函数,以后如果要继续使用该功能则直接使用函数即可,另外,如果要修改需求,只需要修改函数 - 设计函数的优点:
a.简化代码结构,提高应用的模块性
b.提高了代码的复用性
c.提高了代码维护性 - 语法
'''
def 函数名(变量1,变量2....):
函数体
return 返回值
'''
-
说明:
a.def是一个关键字,是definition的缩写,专门定义函数b.函数名:遵循合法标识符的规则和规范即可,尽量做到见名知意
c.(变量1,变量2…):被称为形式参数,是一个参数列表,都只是没有赋值的变量
d.函数体:封装某些特殊的功能
e.return是一个关键字,表示返回,只能用在函数中,表示结束函数,可以单独使用,也可以携带数据,当携带数据,则表示该函数的返回值
f.返回值:常量,变量,表达式
g.函数的定义分为两部分:函数的声明和函数的实现
h.变量1,变量2… 和 return 返回值 可以根据具体的需求选择性的省略
'''
def 函数名(变量1,变量2....):
函数体
return 返回值
'''
# 3.1.无参无返回值
def func1():
print("ok~~~1111")
# 3.2.有参无返回值
def func2(a):
print('ok~~~2222')
# 3.3.无参有返回值
def func3():
print('ok~~~~3333')
return 10
# 3.4.有参有返回值
def func4(a,b):
print('ok~~~~~~4444')
return 'abc'
print('end')
"""
注意:
1.定义函数相当于定义变量,简单来说,将函数加载到了内存中,可以通过debug来看执行过程。
2.一个函数一旦被定义,需要手动调用它,否则无法执行
"""
2、调用
- 1.函数调用的本质:就是使用函数的过程/执行函数内部代码的过程
- 2.调用函数的过程中,一定要注意传参的问题
- 3.形式参数:简称形参,出现在函数的声明部分,实际上就是一个变量,等待实参赋值
- 4.实际参数:简称实参,出现在函数的调用部分,实际上就是一个数据【常量,变量,表达式】,目的是为了给形参赋值
- 5.传参:在调用函数的过程中,实参给形参赋值的过程
#1正常调用
def check1():
print('ok~~~1111',end=' ')
check2()
def check2():
check3()
print('ok~~~2222',end=' ')
def check3():
print('ok~~~3333',end=' ')
print(check1()) # ok~~~1111 ok~~~3333 ok~~~2222 None
#2死锁(恶意调用)
# def a():
# print('aaaaa')
# a()
# a() #RecursionError: maximum recursion depth exceeded while calling a Python object
"""
注意:
1.函数之间可以相互调用
2.代码在执行的过程中,一旦遇到某个函数的调用,则优先执行该函数,当该函数执行完毕好,回到调用函数的地方,继续执行后面的代码
3.一定要避免函数之间的恶意调用
"""
3、参数
- 形式参数:简称形参,定义在函数的声明部分,本质是一个没有值的变量,用于接收实参的值
- 实际参数:简称实参,出现在函数的调用过程中,本质是一个有值的变量或者常量,用于给形参赋值
- 传参:实参给形参赋值的过程
- 参数分类:
- 必需参数
- 默认参数
- 关键字参数
- 不定长参数【可变参数】
#5、参数
#5.1、必须参数:如果形参是必需参数,调用函数的时候,实参和形参的数量和顺序必须保持一致,也需要注意类型的问题。
def func1(a,b):
print(a + b)
func1(1,2) #3
#5.2、默认参数:默认参数的出现,可以简化函数的调用
def add(num1,num2=5):
print(num1 + num2)
add(10)
add(9) #14
def add(num1=0,num2=0):
print(f"{num1} + {num2} = {num1 + num2}")
add() #0 + 0 = 0
add(34) #34 + 0 = 34
add(23,9) #23 + 9 = 32
# 注意3:如果形参有多个,可以必需参数和默认参数混合使用,但是在形参中,默认参数靠后书写
# def test4(a,b = 0,c): # SyntaxError: non-default argument follows default argument
# print(a,b,c)
# test4(6,3)
#5.3、关键字参数:
# 注意1:关键字参数体现在函数的调用部分,通过关键字参数传参,可以调换参数的顺序
# 注意2:调用函数的时候,如果使用关键字参数,则关键字一定要和形参名称保持一致
def func32(name,age=0):
print(f"姓名:{name},年龄:{age}")
func32('aaa') #姓名:aaa,年龄:0
func32('aaa',34) #姓名:aaa,年龄:34
func32(name='aaa') #姓名:aaa,年龄:0
func32(name='aaa',age=6) #姓名:aaa,年龄:6
func32(age=8,name='aaa') #姓名:aaa,年龄:8
# 关键字参数在系统函数中体现较多
print(34,45,5,5,sep='*') # 34*45*5*5 同时体现了默认参数和关键字参数
print(int('110',base=2)) # 6
# 注意1:关键字参数体现在实参中
def check1(name,age,score):
print(f"姓名:{name},年龄:{age},成绩:{score}")
# 注意2:使用关键字参数传参可以不用匹配顺序,但是,关键字需要和形参的变量名保持一致
check1(age=18,name="jack",score=100) # 姓名:jack,年龄:18,成绩:100
# 注意3:在实参中,关键字参数只能书写在参数的后面(这个可以对比默认参数中,在形参中默认参数也是靠后写的注意事项)
def check2(name,age,score):
print(f"姓名:{name},年龄:{age},成绩:{score}")
# check2(name="tom",10,66) # SyntaxError: positional argument follows keyword argument
# check2(10,66,name="tom") # TypeError: check2() got multiple重复 values for argument 'name'
#5.4、不定长参数/可变参数
# a. *x:x将会被当做元组处理,实参可以是任意数量的数据
def func41(*num): # 打包
print(num,type(num))
print("*num:",*num) # 拆包(元组前面加*,元组当中元素会被一个个拆开出来)
func41()
print('#'*30)
func41('abc')
print('#'*30)
func41('abc',45,65,7,8,89,9,True)
'''
() <class 'tuple'>
*num:
##############################
('abc',) <class 'tuple'>
*num: abc
##############################
('abc', 45, 65, 7, 8, 89, 9, True) <class 'tuple'>
*num: abc 45 65 7 8 89 9 True
'''
# b.**x:x将会被当做字典处理,实参必须以key=value的方式传参,key必须是一个标识符
def func42(**num): # 打包
print(num,type(num))
func42()
func42(x=45,y=6,z=67) # {'x': 45, 'y': 6, 'z': 67}
func42(abc='hello')
dict1 = {'a':10,"b":20}
print('check')
#print(f'{**dict1}') #SyntaxError: f-string: invalid syntax (没有这种写法)
func42(**dict1) # {'a': 10, 'b': 20} <class 'dict'>【**dict1是拆包(把字典中的元素拆成键值对),然后再被形参打包】
# 说明:在实际应用中,*和**可以同时使用,作为不定长参数(★★★★★)
def func(*args,**kwargs):
print(args,kwargs)
func()
func(10,20)
func(10,20,name='zhangsan',age=10)
# 注意1:必需参数可以和不定长参数混合使用
def text2(num1,*num2):
print(num1,num2)
text2(10,45,56,67,6,8,7,8) # 10 (45, 56, 67, 6, 8, 7, 8)
# 注意2:如果不定长参数出现在形参列表的前面,则可以结合关键字参数完成传参
def text3(*num1,num2):
print(num1,num2)
# text3(10,45,56,67,6,8,7,8) # TypeError: text3() missing 1 required keyword-only argument: 'num2'
text3(10,45,56,67,6,8,7,num2 = 8) # (10, 45, 56, 67, 6, 8, 7) 8
# 注意3:*xxx在形参列表中只能被使用一次
# def text4(*num1,*num2):
# print(num1,num2)
# text4(45,57,6,77,8)
# 注意4:给**xxx的形参传参,则必须以key=value的方式传参
# 注意5:**xxx在同一个函数的形参中只能出现一次
# def f2(**num1,**num2):
# print(num1)
# f2(x=10,y=20,z=30)
# 注意6:*xxx和**xxx可以同时使用,但是,分别只能使用一次
def f3(*num1,**num2):
print(num1,num2)
f3(45,45,65,7,67) # (45, 45, 65, 7, 67) {}
f3(45,45,65,7,67,a=4,b=467,fag="abc") # (45, 45, 65, 7, 67) {'a': 4, 'b': 467, 'fag': 'abc'}
f3(a=4,b=467,fag="abc") # () {'a': 4, 'b': 467, 'fag': 'abc'}
3.1、形参传递说明(★★★★★★):
- 值传递:传参的时候,传递的是不可变的数据类型,如:int/float/str/tuple/bool,当形参发生修改,对实参没有影响
- 引用传递:传参的时候,传递的是可变的数据类型,如:list/dict/set等,当形参中的元素发生修改,则实参会随着修改
- 具体是引用传递还是值传递,主要看传到形参当中的是可变还是不可变数据
# 1.值传递
def func1(num):
print(f"修改之前,num的地址:{id(num)}") #参数给45时,打印:修改之前,num的地址:1227227492016
num = 100
print(f"修改之后,num的地址:{id(num)}~~~~") #参数给45时,打印:修改之后,num的地址:1227227682256~~~~
temp = 45
func1(temp)
print(f"temp的值:{temp}") #temp的值:45
print("*" * 50)
# 2.引用传递
def func1(num):
print(f"修改之前,num的地址:{id(num)}") #参数给[2,3,4]时,打印:修改之前,num的地址:1227237790912
num[1] = 100
print(f"修改之后,num的地址:{id(num)}~~~~") #参数给[2,3,4]时,打印:修改之后,num的地址:1227237790912~~~~
temp = [2,3,4]
func1(temp)
print(f"temp的值:{temp}") #temp的值:[2, 100, 4]
4、返回值
- 注意1:return是一个关键字,可以单独作为一条语句,只能使用在函数中,表示结束函数
- 注意2:同级的情况,在return的后面添加的任何语句,永远没有执行的机会
- 注意3:return跳出的是函数,break跳出的是当前循环
- 注意4:return还可以返回一个或多个返回值,多个返回值的话则该返回值类型为元组
def show1(a):
print('1111')
if a > 10:
return
print('over')
show1(20) #1111
show1(9) #第一行打印:1111 第二行打印:over
def show23():
return 'abc',10,45
print(show23(),type(show23())) # ('abc', 10, 45) <class 'tuple'>
print(show23) #<function show23 at 0x00000276AF9E2A60> (直接打印函数名的结果,就会打印函数在内存中的位置)
5、封装函数
- 如果要封装一个函数,需要注意两个方面:
- 是否需要设置形参:如果需求中有未知项参与运算,则设置为形参
- 是否需要设置返回值:如果需求运算完是否有结果,如果有结果,则设置返回值
def isprime(num):
if num < 2:
return False
else:
result = True
for n in range(2, num):
if num % n == 0:
result = False
break
return result
r = isprime(11)
print(r) #True
6、匿名函数(简化函数定义格式★★★★★)
- 概念:不再使用def这种标准形式定义函数,而是使用lambda表达式来创建函数,该函数没有函数名,被称为匿名函数,匿名函数也被称为lambda表达式。
- 语法:lambda 形参列表:返回值
- 特点:
-
a.lambda只是一个表达式,用一行代码实现一个简单的逻辑,可以达到对函数的简化【优点】
-
b.lambda主体是一个表达式,而不是一个代码块,只能封装有限的逻辑【缺点】
-
c.lambda拥有自己的命名空间,不能访问自有列表之外或者全局命名空间里的参数
-
#匿名函数
#格式: lambda 形参1,形参2,···,形参n : 函数体 【函数体当中可以是返回值】
# 定义
def func1():
print('111')
print(func1) # <function func1 at 0x106778f70>
#注意匿名函数如果没有形参就在lambda后加上个空格,有多个形参的话,用空格把形参和lambda隔开,形参之间用逗号隔开。
f2 = lambda :print('111!~~~~')
print(f2) # <function <lambda> at 0x106814790> (这里说明匿名函数赋值给那个变量,这个变量就代表了这个匿名函数)
#调用
# 调用
# 方式一:变量 = lambda xx:xxx,通过该变量就可以调用函数
r2 = f2() #111!~~~~ 这里r2代表的是f2所代表的匿名函数的运行结果,因为f2后面有个括号
print(r2) #None
# 方式二:不常用
(lambda :print('111!~~~~'))() #111!~~~~
#关于匿名函数的返回值
# 注意:匿名函数的返回值不需要return
add2 = lambda num1,num2:num1 + num2
r2 = add2(23,9)
print(r2) #32
r = lambda x1,x2 : x1 > x2
print(r(1,2)) # False
print(r(4,2)) # True
6.1、匿名函数在sort()系统功能中的应用
- 说明:
- 普通规则:
列表.sort():将列表中的元素进行升序
列表.sort(reverse=True):将列表中的元素进行降序
工作原理:默认情况下,sort()进行排序的时候,是将列表中的元素两两进行大小比较 - 详细规则:
列表.sort(reverse,key=func)
key:表示自定义排序规则,具体则规则由func函数定义
工作原理:将列表中元素依次取出,传参给func函数,该函数的返回值就是排序的规则,所以要求该函数的返回值必须支持大小比较
- 普通规则:
#sort之使用匿名函数作为规则key
d = [{'姓名':'凯子','成绩':90},{'姓名':'鸽子','成绩':50},{'姓名':'饺子','成绩':70}]
# d.sort() #TypeError: '<' not supported between instances of 'dict' and 'dict'
# d.sort(reverse=True) #TypeError: '<' not supported between instances of 'dict' and 'dict'
d.sort(key=lambda k:k['成绩'])
print(d) #[{'姓名': '鸽子', '成绩': 50}, {'姓名': '饺子', '成绩': 70}, {'姓名': '凯子', '成绩': 90}]