Python阶段性学习之函数
一、函数的定义
函数是代码的一种组织形式。
是组织好的,具有一定的功能,可重复使用的代码块。
二、函数的作用及分类
作用:
1.使得程序变得易维护
2.使程序变得易扩展
3.减少重复的代码,增加代码的复用率
分类:
1.内置函数
在系统内置模块中,已经定义好的函数(不用引入任何模块,直接就能使用),系统内部函数如 max(),min(),map(),print(),input()等函数
2.自定义函数
def 函数名():
三、函数的使用
3.1.原则:函数先定义,再调用
定义阶段:只检测语法,不执行函数体的代码
调用时,才执行定义的函数体代码
3.2.定义函数的语法
def 函数名(形式参数1,形式参数2...):
函数体
return 返回值
3.3.函数的调用语法:
函数名(实际参数1,实际参数2…)
3.4.函数定义注意事项:
1.def 是关键字,后跟一个空格
2.函数名自定义(需要遵循变量名命名规则,见名知意)
3.函数名后的括号和冒号不能丢,括号内有无参数根据情况而定
4.函数内所有代码缩进
3.5.函数定义的三种形式:
有参函数:通过接受外部实际参数传参,进行一系列的操作
无参函数:只是进行函数内部的操作
空函数:什么都不做,内部用pass占位符
3.6.函数调用时的形式:
1.直接调用:函数名加括号
2.表达式形式: 将函数的返回值参与计算
3.当作参数传给另一个函数
四、函数的返回值
return作用:
1.用于函数的返回值
2.结束函数
注意:
1.一个函数没有返回值,系统默认返回None
2.函数可以存在多个返回值,如果一个变量接收多个返回值,返回值之间用逗号隔开,则返回的类型是tuple(元组)类型;也可以使用对应个数的变量接收对应个数的返回值
def func():
print('hello world')
return 1,2,3,'a'
v = func()
print(v,type(v))
>>>
'hello world'
(1,2,3,'a') < class 'tuple'>
五、函数的参数
1.形式参数:在函数定义阶段,括号内定义的参数
2.实际参数:在函数调用时,括号内传入的值
在调用函数阶段会将实参的值传递给形参,这种传递关系只在函数调用时生效,在函数执行完后失效
3.位置参数:
站在形参角度是位置形参
函数定义阶段,从左到右的顺序依次定义的形参
定义的形参,必须被传值,多一个少一个都不行
站在实参角度是位置实参
调用阶段,从左到右依次传入的值
与形参一一对应
def func1(a,b):
print(f'a = {a},b = {b}')
func1(2,3)
>>>
a = 2,b = 3
4.关键字参数: 按照key = value 的形式定义的参数
特点: 可以打乱顺序,但仍然能为指定的形参传值
def func2(a,b,c):
print(f'a,b,c')
func2(1,c = 5,b = 2)
>>>
1,2,5
注意:
实参中位置参数和关键字参数可以混合使用,但是必须遵循一下原则:
1.位置参数必须放在关键字参数前面
2.不能对同一个形参重复传值
5.默认参数:
在函数定义时,已经为形参赋值,该形参称为默认参数
特点:
1.调用时可以不用传值 不传值默认为定义时的值
2.位置形参必须放在默认形参的前面
def func(x,y,z = 100):
print(x,y,z)
func(10,23)
>>>
10,23,100
func(10,23,123)
>>>
10,23,123
## 输入某个人的爱好存入列表中并输出
def add_hobby(name,hobby,hobbies = []):
hobbies.append(hobby)
print(f'{name}的爱好是{hobbies}')
add_hobby('Tom','read book')
add_hobby('Lily','shopping')
add_hobby('Curey','play games')
>>>
Tom的爱好是['read book']
Lily的爱好是['read book','shopping']
Curey的爱好是['read book','shopping','play games']
##改进实参传入方式
add_hobby('Tom','read book')
add_hobby('Lily','shopping',[])
add_hobby('Curey','play games',[])
>>>
Tom的爱好是['read book']
Lily的爱好是['shopping']
Curey的爱好是['play games']
6.可变参数(*args,**kwargs)
定义:指的是在函数调用阶段,实参的个数是不固定的
*args:
在调用时以位置参数形式进行传参,可以给0个,1个…多个,任意类型的参数都可以
args 会当成元组类型来处理,传进来的每一个参数都会被当作元组的一个元素
**kwargs:
要求调用时接收的实参必须是已键值对类型key = value(关键字参数形式)传参,可以给0,1,多个键值对
def func(*args):
print(args)
print(type(args))
func(1,2,3,'a')
>>>
(1,2,3,'a')
<class 'tuple'>
## *args不能传关键字参数形式 否则会报异常
func(b = 1)
>>>
TypeError: func() got an unexpected keyword argument 'b'
def func(**kwargs):
print(kwargs)
print(type(kwargs))
func(a = 1, b = 2)
>>>
{'a': 1, 'b': 2}
<class 'dict'>
## **kwargs 不接收位置参数形式的传参,否则报异常
func(1,2)
>>>
TypeError: func() takes 0 positional arguments but 2 were given
def func(*args,**kwargs):
print(args,kwargs)
func(1,2,3,a = 1,b = 3)
>>>
(1,2,3),{'a': 1, 'b': 3}
六、函数的封装技巧
1.遇到判断是否字样 返回值返回布尔值 增加函数的可复用性 ,而不要直接打印输出
#封装函数 判断一个数是否为偶数
def is_even_num(n):
return n % 2 == 0
res = is_even_num(12)
print(res)
>>>
Ture
七、函数的嵌套及作用域
1.函数的嵌套: 在函数内又定义了函数
def func():
def funcIn():
print('---funcIn---')
funcIn()
print('---func---')
res = func()
>>>
---funcIn---
---func---
2.命名空间
指的是保存程序中变量名和值的地方。
命名空间的本质是一个字典,用来记录变量名称和值
字典的key 是变量名称,字典的value 对应的是变量的值
如{’name’:‘zs’,‘age’:12}
分类:
内置命名空间
全局命名空间
局部命名空间
系统的加载顺序:
内置—> 全局—> 局部
调用时搜索顺序:
局部—> 全局—> 内置
3.函数的作用域: 就是作用范围 (在哪个范围内生效)
LEGB
L—Local 局部作用域
E—Enclosing 闭包作用域 闭包函数
G—Global 全局作用域
B— Builtin 内置作用域
c = 10
def func4():
c = 100
# 外部函数中,无法访问到内部函数a
# print(a)
def func2(b = 20):
a = 10
print(c)
func2()
func4()
>>>
100
注意:作用域关系是在函数定义阶段就已经固定死了,与调用位置无关!
def f1():
print(xxx)
xxx = 112
def f2():
xxx = 233
f1() # 不管f1在哪调用,都是去找定义阶段的作用域关系,与上面xxx = 233没有任何关系
# 除非将f1函数定义在f2函数内部 搜索时优先搜索局部作用域 此时输出结果为 233
f2()
>>>
112
数参数传递问题:
如果参数是基本类型:
属于值传递,在函数内部操作,不影响结果
如果参数是可变类型
属于地址传递,在函数内部操作,源数据发生变化
a,b = 10, 20
def swap(x,y):
x,y = y,x
print(f'交换前:a = {a} b = {b}')
swap(a,b)
print(f'交换后:a = {a} b = {b}')
>>>
交换前:a = 10 b = 20
交换后:a = 10 b = 20
list1 = [1,2]
def swap(list2):
list2[0],list2[1] = list2[1],list2[0]
print(f'交换前:{list1}')
swap(list1)
print(f'交换后:{list1}')
>>>
交换前:[1, 2]
交换后:[2, 1]
八、匿名函数与递归函数
8.1 匿名函数:又叫 lambda表达式。
定义方式:
lambda 参数:表达式
参数可以有多个,用逗号隔开,返回值和正常的函数一样可以是任意的数据类型
特点:
在定义时使用一次,且函数体只有一行
匿名函数的使用场景:
功能简单,可复用性不强的函数,可以做成匿名函数
注意:
不能包含循环、return,可以包含 if…else…
f = lambda a,b: a + b
print(type(f))
v = f(1,2)
print(v)
>>>
<class 'function'>
3
lst1 = [1,2,3]
lst3 = list(map(lambda x:x**2,lst1))
print(lst3)
>>>
[1,4,9]
8.2 递归函数
递归本质就是一个循环的过程
递归调用:
直接或者间接的调用自己,这种调用方式,递归调用
注意:
在递归调用中,一定要有递归的结束条件,如果没有,则会导致递归错误
递归执行的本质:
压栈操作:反复入栈
为什么使用递归:
在某些情况下,基于递归使用重复的过程比循环更简单
#给定一个嵌套列表,取出列表中的每个元素输出打印
lst1 = [1, [2, [3, [4, [5, [6, [7, [8, [9, ]]]]]]]]]
lst2 = []
def func(list1):
for i in list1:
if type(i) is list:
func(i)
else:
lst2.append(i)
return lst2
res = func(lst1)
print(res)
>>>
[1,2,3,4,5,6,7,8,9]
# 斐波那契数列
# 1,1,2,3,5,8,13...
#求斐波那契数列第 n 项的数
def rec_fibo(n):
if n <= 2:
return 1
else:
return rec_fibo(n-1) + rec_fibo(n-2)
res = rec_fibo(10)
print(res)
>>>
55