一、函数
函数特点:是'组织好'的,可'重复使用'的,用来实现单一或相关联功能的'代码段'
作用:函数能提高应用的'模块性',和代码的'重复利用率'
函数分类:Python'内建'函数、用户'自定义'函数
二、函数定义和调用
(1)定义
1)函数代码块以' def 关键词'开头,后接函数名'最好有意义'和圆括号'([args])'
备注: 圆括号之间可以用于'定义参数'
2)函数的'第一行语句'可以选择性地使用文档字符串("""""")用于存放'函数说明'
3)函数内容以'冒号起始(:)',并且'缩进'
4)return [表达式] 结束函数,选择性地'返回一个值(也可以是函数)给调用方'
备注: 不带表达式的return相当于'返回 None'
(2)调用
说明: 函数必须'先定义','后续'才能'使用'
① 无参传递
# 定义函数
def function():
print('java')
print('Linux')
# 调用函数-->函数名来调用
function()
② 有参传递
三、函数的参数类型
(1)参数的两个概念
1)形参: '定义'函数的时候的变量,'占位符'作用,可以'任意起名'
2)实参: '调用'函数时,传入的'变量或值'
(2)形参的类型
1)'位置'参数('position')
2)'默认'参数('default')
3) 参数'收集'-->收集'实参'其余的传递参数
-- 行参前面带一个'*'-->意味着该参数'可接收多个参数',多个参数被'当作元组的元素'传入-->收集到'元组'中
-- 行参前面带两个'*'-->意味着该参数'可接收多个参数',多个参数被'当作字典的元素'传入-->收集到'字典'中
① 位置参数
位置参数:须以'正确的顺序'传入函数,调用时的'实参'必须和声明时的'形参'-->'个数想等'
② 默认参数
默认参数:调用函数,如果用户'没有传递参数',则会'使用默认参数'
格式:'形参名 = 默认值'
③ 元组收集参数
④ 字典收集参数
(3)实参的类型
① keywords关键字实参
② 逆向收集列表的元素
++++++++'前提条件'++++++++
1)程序已有'list'、'tuple'、'dict'等对象的前提下
2)在'调用函数','传入该对象'的时候,将该对象的元素'拆分'后传给行参
备注1: 如果是'list、tuple',按照'位置参数'形式传递给形参
备注2: 如果是'dict',按照'关键字参数(keywords)'传给行参
逆向收集表现形式: 传实参时候,'list'、'tuple'前加一个'*'号;'dict'前加两个'*'号-->表示对象'拆分后'传递
备注: 如果'对象'传递的时候没有加'*',表示作为一个'整体传入'的
+++++++++++'理解'+++++++++++
add(*list) '<==>' add(1,2)
备注: '元组同上'
③ 逆向收集字典的元素
+++++++++++'理解'+++++++++++
add(*tuple1) '<==>' add(age=10,name='wzj')
备注: '将字典元素'拆分成一个一个的'关键字keywords'参数进行传递
④ 逆向收集传递对象
备注: 这个对象,一般指'list'、'tuple'、'dict'
说明: 这里以'dict'为例子
1)列表对象
数目: 参数收集成一个'元组',是'不可变对象'
2)字典对象
(4)规则
1)如果混合使用'关键字参数'和'位置参数',关键字参数必须位于位置参数'之后'
'调用'-->add(1,key=2)
2)默认值参数也属于'关键字参数'的一中,定义在'行参列表'的最后
'定义'-->add(num,key=2)
3)Python只允许一个函数'最多只有一个'支持"普通"参数收集的形参
理解: '同一种类型'最多只能'出现一次'
(5)特殊案例
① 收集不同类型的可变参数
def test(*hello, **key_value):
print(type(hello))
print(type(key_value))
for i, j in key_value.items():
print(i,j)
test(11, 12, 13, name=24, age=28)
② 参数收集和关键字参数
# 特点: 可变参数'不必'处于'行参列表的末尾'
def test(*num,key):
print(type(num))
for i in num:
print(i)
print('key=%d' %(key))
# 在有多变参数的情况下,如果要给后面的形参传递参数值,必须使用关键字参数,否则程序会把所传入的值当成num的数值!
test(1,2,3,4,key=24)
(6)小结
四、参数传递机制
说明: 参数传递都是'值传递',对于对象而言,'值'就是地址值,是'引用传递'
回顾: 数据结构中只有'list'、'dict'是可变对象,有关'习题'也是关于'这两个的'
"""
python 函数的参数传递:
不可变类型:类似 c++ 的值传递,如 整数、字符串、元组;
如fun(a),传递的只是a的值,没有影响a对象本身。比如在fun(a)内部修改a的值,只是修改另一个复制的对象,不会影响a本身。
可变类型:类似 c++ 的引用传递,如 列表(list)、字典(dict);
如 fun(la),则是将la真正的传过去,修改后fun外部的la也会受影响!
小结:python 中一切都是对象,严格意义我们不能说"值传递"还是"引用传递",我们应该说传不可变对象和传可变对象。
"""
说明: 通过'可变对象'传入函数,'修改了'某些数据
五、函数返回值
说明: 这里主要'讲解'一下'多返回值'
1)多返回值的'定义'-->','号隔开
1)多值'封包成'列表,一个变量来接收
2)直接'多个变量'接收'多返回值' -->'解包'
① 封装成元组
② 多变量接收
备注: 多变量接收必须'保证'和返回值的'个数相等'
六、递归函数
注意: '递归调用'一定要向'已知方向'进行
七、局部函数
① 定义
1)定义:函数体'内部'定义的函数,被'放在函数体内'定义的函数称为'局部函数'
2)备注:局部函数是'对外隐藏'的
八 高级函数知识点
① 函数变量
② 函数作为函数的形参
③ 函数作为函数的返回值
说明: 在'局部函数的'章节就体会了一下
八、变量的作用域
① 现象
说明:当'局部变量'和'全局变量重名'的时候,全局变量'会被覆盖',而上面的'局部变量'的'使用在声明之前',所以出错
备注: 先理解'a=4',然后'print'
细节: 不通过'参数传递',理论上是'无法修改'局部变量
1)如果'只是使用'这个变量,即'只读不写'
2)以下面代码为例:如果'fun函数'只是'使用a',则可以'直接用',程序会根据'上文所说的顺序'来寻找这个变量。
3)但如果要'对a进行修改',而在对'a进行赋值之前'使用b,这时候程序会'报错说变量未定义',而不是去找外围的a
备注: 如果'有赋值等操作时'则会报错说'变量未定义'
4)所以当内部作用域'想修改'外部作用域的变量时,就要用到'global'和'nonlocal'关键字了
备注: global是'全局的',nonlocal是'上一层'的
② 需求:局部变量和全局变量重名的时候,在函数内部都要使用局部变量和全局变量该怎么办?
def fun():
global a; #声明使用的是全局变量-->4
print('a:', a)
#对比此处注释前后的差别-->说明:这里是对全局变量进行赋值,而不是重新定义一个局部变量!
a = 5
print('a',a)
a = 4
fun()
九、练习题
练习1
"""
编写一个函数cacluate, 可以接收任意多个数,返回的是一个元组.
元组的第一个值为所有参数的平均值, 第二个值是大于平均值的所有数.
#列表转化为元组!
"""
def calcluate(args):
# 说明:元组没有办法添加-->不可变!-->可以通过列表来转换
# tp[index]和tp.append()的区别?
# 前者如果不存在该索引,就会报错-->相当于修改!
tp = []
# (1)计算平均值-->两个内置函数!
# print(type(args))-->所传即所得!
tp.append(sum(args) / len(args))
for i in args:
if i > tp[0]:
tp.append(i)
return tuple(tp)
# (1)获取数据
accept = input('Please input number separated by spaces:')
# 说明:列表生成式
number_list = [int(i) for i in accept.split()]
# (2)调用函数
print(calcluate(number_list))
练习2
"""
编写函数, 接收一个列表(包含30个整形数)和一个整形数k, 返回一个新列表.
函数需求:
- 将列表下标k之前对应(不包含k)的元素逆序;
- 将下标k及之后的元素逆序;
[1,2,3,4,5] 2 [2,1,5,4,3]
"""
def fun(alist,k):
if k<0 or k > len(alist):
return 'error key'
#细节:相同类型才可以叠加!-->列表的切片特性、索引特性!
return alist[:k][::-1] + alist[k+1:][::-1]
print(fun([1,2,3,4,5],2))