函数使用背景
假设老板让你写一个监控程序,24小时全年无休的监控你们公司网站服务器的系统状况,
当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警,
你掏空了所有的知识量,写出了以下代码
从图中可以看出上述代码存在了两个问题
1.代码冗余
2.维护性差
我们如何解决这个问题?请看下图
从上图可以看出:使用函数可以带来以下好处
- 减少重复代码
- 使程序变的可扩展
- 使程序变得易维护
函数定义
函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,具体区别,我们后面会讲,编程中的函数在英文中也有很多不同的叫法。在BASIC中叫做subroutine(子过程或子程序),在Pascal中叫做procedure(过程)和function,在C中只有function,在Java里面叫做method。
定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
代码表现形式:
def 函数名(参数1,参数2):
''' 函数注释'''
print('函数体')
return 返回值
定义: def关键字开头,空格之后接函数名和圆括号,最后还要加一个冒号。
def是固定的,不能变。
函数名:函数名是包含字母,数字,下划线的任意组合,但是不能以数字开头。虽然函数名可以随便取名,但是一般尽量定义成可以表示函数功能的。
#下面这段代码 a,b = 5,8 c = a**b print(c) #改成用函数写 def calc(x,y): res = x**y return res #返回函数执行结果 c = calc(a,b) #结果赋值给c变量 print(c)
函数参数
使用参数可以让你的函数更灵活,根据调用时传参的不同来决定函数内部的执行流程
1.实参和形参
形参: 是函数定义时候定义的参数
只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。
实参: 调用的时候传进来的参数
可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,
以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
2.参数传递
可以传递多个参数,多个参数之间用逗号隔开。
调用函数时传参数有两种方式:
1.按照位置传参数
2.按照关键字传参数
正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可(指定了参数名的参数就叫关键参数),
但记住一个要求就是,关键参数必须放在位置参数(以位置顺序确定对应关系的参数)之后
用法:1.位置参数必须在关键字参数的前面
2.对于一个参数只能赋值一次
def stu_register(name, age, course,country): print("----注册学生信息------") print("姓名:", name) print("age:", age) print("国籍:", country) print("课程:", course) stu_register("alex", 23, "python", "china") # 按照位置传递参数 stu_register("alex", 23, course = "python", country = "china") # 按照位置和关键字传递参数 # 以下两种使用方法不正确 stu_register("alex", "python", 23, "china") # 位置不对 stu_register("王山炮", 22, age=25,country='JP') # age参数被定义两遍
3.默认参数
用法:为什么要用默认参数?将变化比较小的参数设置成默认参数(比如中国网站上注册用户功能,像国籍这种信息,就可以设置个默认值参数country=CN)
定义:默认参数可以不传,不传的时候用的就是默认值,如果传,会覆盖默认值。
def stu_register(name, age, course, country = "CN"): print("----注册学生信息------") print("姓名:", name) print("age:", age) print("国籍:", country) print("课程:", course) stu_register("alex", 23, "python") # 使用默认参数 stu_register("Bill", 23, "python", "Japan") # 覆盖默认参数
默认参数使用注意点——可变数据类型 (字符串,数字等)
默认参数使用注意点——不可变数据类型(列表,字典等)
4.动态参数
按位置传值多余的参数都由args统一接收,保存成一个元组的形式
按关键字传值接受多个关键字参数,由kwargs接收,保存成一个字典的形式
元祖
def stu_register(name, age, *args): # *args 会把多传入的参数变成一个元组形式 print(name,age,args) stu_register("Alex",22) # 输出 # Alex 22 () #后面这个()就是args,只是因为没传值,所以为空 stu_register("Jack",32,"CN","Python") # 输出 # Jack 32 ('CN', 'Python')
字典
def stu_register(name, age, *args, **kwargs): # *kwargs 会把多传入的参数变成一个dict形式 print(name,age,args,kwargs) stu_register("Alex",22) #输出 #Alex 22 () {}#后面这个{}就是kwargs,只是因为没传值,所以为空 #传参数的时候:必须先按照位置传参数,再按照关键字传参数 stu_register("Jack",32,"CN","Python",sex="Male",province="ShanDong") #输出 # Jack 32 ('CN', 'Python') {'province': 'ShanDong', 'sex': 'Male'} def fun(a,b,**kwargs): print(a,b,kwargs) # 按照关键字传参数 fun(a = 10,b = 20,cccc= 30,dddd = 50) # 输出 # 10 20 {'cccc': 30, 'dddd': 50} def f(a,b,*args,defult=6,**kwargs): #位置参数,*args, 默认参数,**kwargs # print(a,b,args,defult,kwargs) return a,b,args,defult,kwargs # 传参数的时候:必须先按照位置传参数,再按照关键字传参数 print(f(1,2,7,8,ccc=10,der=5)) # 输出 # 1,2,(7,8),6,{'ccc':10,'der':5}
动态参数拆包与装包
1.*args
def run(a,*args): #第一个参数传给了a print(a) # 1 # args是一个元组,里面是2和3两个参数 print(args) # (2,3) # *args是将这个元组中的元素依次取出来 print("对args拆包") print(*args) # 2 3 *args 相当于 a,b = args print("将未拆包的数据传给run1") run1(args) # ((2, 3),) 和 (2, 3) print("将拆包后的数据传给run1") run1(*args) # (2, 3)和2 3 def run1(*args): print("输出元组") print(args) print("对元组进行拆包") print(*args) run(1,2,3) #后面的2和3
输出结果
1 (2, 3) 对args拆包 2 3 将未拆包的数据传给run1 输出元组 ((2, 3),) 对元组进行拆包 (2, 3) 将拆包后的数据传给run1 输出元组 (2, 3) 对元组进行拆包 2 3 """ 理解这段代码需要把握住下面几点: 1. 形参中的*args其实真正接收数据的args,它是一个元组,把传进来的数据放在了args这个元组中。 2. 函数体里的args依然是那个元组,但是*args的含义就是把元组中的数据进行拆包,也就是把元组中的数据拆成单个数据。 3. 对于args这个元组,如果不对其进行解包,就将其作为实参传给其它以*args作为形参的函数时,args这个元组会看看作一个整体,作为一个类型为元组的数据传入。 """
2. **kwargs
def run(**kwargs): """ 传来的 key = value 类型的实参会映射成kwargs里面的键和值 kwargs是一个字典,将关键字参数以键值对的形式 """ print(kwargs) # {'a':1,'b':2} print("对kwargs拆包") # 此处可以把**kwargs理解成对字典进行了拆包,{"a":1,"b":2}的kwargs字典又 # 被拆成了a=1,b=2传递给run1,但是**kwargs是不能像之前*args那样被打印出来看的 run1(**kwargs) # 1 2 # print(**kwargs) def run1(a, b): # 此处的参数名一定要和字典的键的名称一致 print(a, b) run(a=1, b=2) d = {'a':1,'b':2} run(**d) # 输出结果与执行run(a=1, b=2)一样
输出结果
{'a': 1, 'b': 2} #run(a=1,b=2) 对kwargs拆包 1 2 {'a': 1, 'b': 2} #run(**d) 对kwargs拆包 1 2 """ def run(**kwargs):#传来的 key = value 类型的实参会映射成kwargs里面的键和值 # kwargs是一个字典,将未命名参数以键值对的形式 print(kwargs) print("对kwargs拆包") # 此处可以把**kwargs理解成对字典进行了拆包,{"a":1,"b":2}的kwargs字典又 # 被拆成了a=1,b=2传递给run1,但是**kwargs是不能像之前*args那样被打印出来看的 run1(**kwargs) #print(**kwargs) def run1(a,b): #此处的参数名一定要和字典的键的名称一致 对于kwargs这个字典,如果不对其进行解包,就将其作为实参传给其它以**kwarg作为形参的函数时,kwarg这个字典会看看作一个整体,作为一个类型为字典的数据传入 """
总结:
函数返回值
函数外部的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回
1. 首先返回值可以是任意的数据类型。
2..函数可以有返回值:如果有返回值,必须要用变量接收才有效果
也可以没有返回值:
1.当不写return的时候,函数的返回值为None
2.当只写一个return的时候,函数的返回值为None
3. 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束
def stu_register(name,age,course): print(name, age, course) # Peiqi 29 安保 return [name, age] # 返回列表形式 status = stu_register('Peiqi',29,'安保') print(status) # ['Peiqi', 29] def stu_register(name,age,course): print(name, age, course) # Peiqi 29 安保 status = stu_register('Peiqi',29,'安保') print(status) # 无返回值时,返回None def stu_register(name,age,course): print(name, age, course) # Peiqi 29 安保 return print(name, age) # 此句不会执行 status = stu_register('Peiqi',29,'安保') print(status) # return后面没有值,返回None