目录
一、函数基础
-
在一个完整的项目中,某些功能会被反复使用,那么将这部分功能对应的代码提取出来,当需要使用功能的时候直接使用。
-
本质:对一些特殊功能的封装。
-
优点:
1. 简化代码结构,提高应用的效率
2. 提高代码复用性
3. 提高代码的可读性和可维护性 -
建议:但凡涉及到功能,都尽量使用函数实现。
1.1 定义、调用函数
1.1.1 定义函数
-
语法:
def 函数名(参数1,参数2,参数3…):
函数体
返回值 -
说明:
1、函数由两部分组成:声明部分和实现部分;
2、def是关键字,是define的缩写,表示定义的意思;
3、函数名:类似于变量名,遵循标识符的命名规则,尽量做到顾名思义
4、():表示的参数列表的开始和结束
5、参数1,参数2,参数3… :参数列表【形式参数,简称为形参】,其实本质上就是一个变量名,参数列表可以为空
6、函数体:封装的功能的代码
7、返回值:一般用于结束函数,可有可无,如果有返回值,则表示将相关的信息携带出去,携带给调用者,如果没有返回值,则相当于返回None。def fn(): print('fn')
1.1.2 函数的调用
-
格式:函数名(参数列表)
-
注意:
1、当定义好一个函数之后,这个函数不会自动执行函数体,必须要调用才会执行;
2、当调用函数的时候,参数列表需要和定义函数时候的参数列表保持一致;
3、一个函数可以被多次调用;
4、当在同一个py文件中定义多个同名的函数,最终调用函数,调用的最后出现的函数(覆盖:函数名类似于变量名,相当于变量的重新赋值);
5、在一个自定义的函数内部也可以调用函数;
6、自定义函数必须先定义,然后才调用, 否则报NameError。def sums(a, b): s = a + b print(s) sums(2, 3)
1.2 return返回值
-
作用:表示一个函数执行完毕之后得到的结果。
-
使用:return,表示结束函数,将函数得到的结果返回给调用者。
-
注意:
1、用于终止函数。在同一个代码块中,如果在return后面出现语句,则永远不会被执行;
2、如果一个函数有返回值,要么采用变量将返回值接出来,要么将整个函数的调用直接参与运算;如果一个函数没有返回值,则这个函数的调用不能直接参与运算。def add(num1,num2): sum1 = num1 + num2 return sum1 r = add(10,20) print(r) 1、有返回值,直接参与运算: total = add(1,2) + 5 print(total) ---> 8 2、无返回值,不能直接参与运算: total1 = func(1,2) + 5 print(total1) ---> 报错
3、如果一个函数没有返回值,则整体计算的结果为None,默认返回None;
4、如果一个函数体中有分支,设置了返回值,最好每一个分支都有一个返回值。不同分支返回的数据类型可以是不相同的。def is_leap(year): if year%4==0 and year%100!=0 or year%400==0: return True else: return False r = is_leap(2020) print(r)
5、一个return可以同时返回多个数据,被当做元组处理。
def change(a, b): return a, b r = change(2, 3) print(r) ---> (2, 3)
6、交换两个变量值。
def change(a, b): return b, a r = change(2, 3) print(r) --- > (3, 2)
7、返回值不建议使用中文。
-
总结:
自定义一个函数
是否需要设置参数:是否有未知项参与运算
是否需要设置返回值:是否需要在函数外面使用函数运算之后的结果
二、函数的参数
2.1 参数分类
-
参数列表:如果函数所实现的功能涉及到未知项参与运算,此时就可以将未知项设置为参数
-
分类:
形式参数(形参):在函数的声明部分,本质就是一个变量,用于接收实际参数的值 。实际参数(实参):在函数调用部分,实际参与运算的值,用于给形式参数赋值。
-
传参:实际参数给形式参数赋值的过程,形式参数 = 实际参数。
2.2 参数类型
2.2.1 必需参数(位置参数)
- 位置参数:调用函数的时候必须以正确的顺序传参,传参的时候参数的数量和形参必须保持一致。调用函数的时候必需传参,顺序一致,数量一致。
def f1(x, y): print(x, y) f1(3, 4)
2.2.2 默认参数
- 在参数列表中,如果所有的形参都是默认参数,正常使用;如果默认参数值只有一个,则只能出现在参数列表的最后面(形参、实参都要在最后)。
- 在形参设置默认参数,如果传参,则使用传进来的数据,如果不传参,则使用默认数据。
1、默认参数未传参: def f2(x, y, z=5): print(x, y, z) ---> 3,4,5 f2(3, 4) 2、默认参数传参: def f2(x, y, z=5): print(x, y, z) ---> 3,4,6 3,4,7 f2(3, 4, 6) f2(3, 4, z=7) 3、报错: def f2(x, y, z=5): print(x, y, z) f2(3, z=4, 6)
2.2.3 关键字参数
- 关键字参数:在函数调用时,实参直接赋值给形参,可不按形参位置顺序,使用关键字进行自动的匹配。
- 如果只有部分实参赋值,未赋值的必须放左边,按形参位置赋值。
def f3(x, y, z): print(x, y, z) ---> 3,5,4 f3(3, z=4, y=5)
2.2.4 不定长参数
1、*参数名:
- 作用:用来接收任意多个位置参数(含0个),接收生成一个元组。变量名就相当于元组名。
def f4(*args): print(args) f4() ---> () 空元组 f4(1) ---> (1,) f4(1,2,3) ---> (1,2,3)
2、**参数名
- 作用:接收不定长的关键字参数,接收生成一个字典,变量名就相当于字典名。
def f6(x, y, **kwargs): print(x, y, kwargs) f6(1, 5, z=7, k=8) ---> 1 5 {'z': 7, 'k': 8}
3、参数通配:接收任意参数
- 可接收任意类型元素
def f8(*args, **kwargs): print(args,kwargs) f8(1,2,3,4,x=5,y=6)
2.2.5 参数的书写顺序
- 形参:位置参数,*args,默认参数,**kwargs
- 实参:位置参数,关键字参数
三、匿名函数【重点】
3.1 匿名函数
-
不再使用def这种的形式定义函数,使用lambda来创建匿名函数。
-
特点:
1、lambda只是一个表达式,比普通函数简单。
2、lambda一般情况下只会书写一行,包含参数,实现体,返回值。
3、匿名函数本身是没有函数名,将整个lambda表达式赋值给一个变量,然后将这个变量当做函数使用。
4、在匿名函数中也可以使用关键字参数、默认参数。 -
语法: lambda 参数列表 : 实现部分
1、使用位置参数: f2 = lambda n:n**2 print(f2(3)) ---> 9 2、使用关键字参数: f2 = lambda n:n**2 print(f2(n=3)) ---> 9 3、使用默认参数: f2 = lambda n=3:n**2 print(f2()) ---> 9
-
用匿名函数排序:
按年龄升序排序:l = [ {'name': '哈哈', 'age': 30}, {'name': '呵呵', 'age': 20}, {'name': 'didi', 'age': 22}, {'name': '喔喔', 'age': 25}, ] l.sort(key=lambda d:d['age']) # 匿名函数 print(l)
3.2 回调函数
- 函数调用:只要是指向函数的变量,都可以调用该函数。
def fm(x, f): n = f(x) print(n) ---> 100 fm(10, lambda m:m*m) 案例2: def fm(x, f): n = f(x) print(n) ---> 121 def f6(m): m= m + 1 return m*m fm(10, f6)
- 封装一个sort功能的函数:
l = [ {'name': '哈哈', 'age': 30}, {'name': '呵呵', 'age': 20}, {'name': 'didi', 'age': 22}, {'name': '喔喔', 'age': 25}, ] def my_sort(l1, key=None): #冒泡排序 for i in range(len(l1)-1): for j in range(len(l1)-1-i): left = l1[j] right = l1[j+1] if key: left = key(left) right = key(right) if left > right: l1[j], l1[j+1] = l1[j+1], l1[j] my_sort(l, key=lambda d:d['age']) print(l)
四、值传递和引用传递【面试题】
-
值传递:传参的过程中传递的是值,一般指的是不可变的数据类型,number,tuple,string
-
引用传递:传参的过程中传递的是引用,一般指的是可变的数据类型,list,dict, set。引用传递本质上传递的是内存地址。
-
引用传递:
l = [10,20,30,40] def func2(list1): list1[0] = 100 func2(l) print(l[0]) ---> 100
-
值传递:
temp = 20 def func1(a): a = 10 func1(temp) print(temp) ---> 20,无返回值,temp值未改变!
-
补充:
age = 10 person = {'name':'哈哈', 'age':22} def change_age(age1, person1): age1 += 1 # 使用的是在函数内部重新定义的新变量,和函数体外的无关 person1['age'] += 1 change_age(age, person) print(age, person) ---> 10 {'name': '哈哈', 'age': 23}