1.函数的理解
提升代码的复用,减少代码的重复编写
2.函数的定义与调用
#函数的定义
def 函数名(形参)
函数体
#接下来我们定义一个判断质数的函数来深入了解 函数构造
def is_prime(num):
for i in range(2,num):
if num % i == 0
return False #如果被整除了 则证明不是质数 返回False
else:
return True #如果不能被整除 则返回 True
#函数已经定义好了,如何调用呢?
#通过定义的函数名称来调用函数
num = int(input()) #接受用户输入的数字
result = is_prime(num) #给函数传入实参,使用return接收返回值
print(result)
3.函数的形参与实参
上面案例中提到了函数的形参与实参,接下来我们深入了解一下
- 形参:形式参数,声明函数 用一个变量名代表这里需要传入一个真实值
- :param num :形式参数num 没有具体值
- 函数声明时,出现在小括号中,多个形参使用逗号分隔开
- 形参没有具体值,需要接受实参
- 实参:实际参数,调用函数时,使用的真实值
- 函数调用时,出现在小括号中,多个实参使用逗号分隔开
- 实参有具体值,传递给形参
4.函数的返回值
根据上面案例我们可以得知:
返回值就是函数的输入,经过函数内部的计算,将计算结果返回,函数执行结束
注意事项
- 如果没有写return
- 返回None
- 函数运行到return
- 后续代码不再继续执行
- return后面可以跟多个值
- 返回一个元组
5.函数参数类型
1)位置参数
按照顺序依次赋值
def my_fun(num1,num2,num3): #这里的形参没有具体值
print(num1,num2,num3)
#调用我们定义的函数
my_fun(a,b,c) #这里我们三个实参会按照顺序依次照应num1,num2,num3
#输出 a,b,c
2)默认参数
拥有默认值,如果赋予新值,则使用新值,否则使用默认值
def my_log(string, m=1, n=2):
print('打印{}'.format(string), m, n)
my_log('213') #给string传入'213'则会打印出'213',m和n没有传入参数,则输出默认值,1和2
#输出 '213',1,2
def my_log(string, m=1, n=2):
print('打印{}'.format(string), m, n)
my_log('213',2,3) #当我们给默认参数赋值时,则会打印出我们传入的值
#输出'213',2,3
3)关键字参数
通过形参名给形参赋值
def my_log(string, m=1, n=2):
print('打印{}'.format(string), m, n)
my_log('213',n=3) #由于参数会按照顺序依次传入,我们不给m赋值,想直接给n赋值是办不到的,此时
#使用关键字传参就可以跳过m,直接给n赋值。
#输出'213',1,3
4)可变参数:
- 可变元组参数
def my_fun(m, *args): #*args 可以接手多个参数并转换为元组 print(m, *args,type(m),type(args)) my_fun(1,2,3,1,2) #调用函数,输出1 2 3 1 2 <class 'int'> <class 'tuple'>
- 可变字典参数
def my_fun(**kwargs):
total = []
for key, item in kwargs.items():
total += item
return total
dict_test = {'1': 'a', '2': 'b', '3': 'c', '4': '5'}
print(my_fun(name='1'))
print(my_fun(name='1', **dict_test)) #要传入字典需要 **进行系列解包操作
# 输出
# ['1']
# ['1', 'a', 'b', 'c', '5']
6.函数变量的作用域
在声明以后的代码区域才可以使用
循环外部不要使用循环内部定义的局部变量
非强制
循环内部定义的局部变量 作用域是函数内部
函数外部不能使用
函数内部可以直接获取函数外部定义的变量
函数内部修改全局变量用global
函数内部修改外部函数的局部变量用nonlocal
7.递归函数
什么是递归函数?
1、如果通过一个对象自身的结构来描述或部分描述该对象,就称为递归。
2、递归函数即自调用函数,在函数内部直接或间接地自己调用自己,即函数的嵌套调用是函数本身
递归函数的原理
1、递归函数之所以能够实现,关键是系统使用堆栈来保存函数调用中的传值参数、局部变量和函数调用后的返回地址。函数自身调用进行堆栈:系统把有关参数和地址压入堆栈,一直递推到满足终止条件,找到问题的最基本模式为止。然后进行回归:系统从堆栈中逐层弹出有关参数和地址,执行地址所指向的代码,一直到栈为空为止,问题得到解决。
递归函数的优缺点
随着递归深度的增加,需要维护很大的函数调用栈,开销太大
效率较低,尽量不要用
优点:
1、递归使代码看起来更加整洁、优雅;
2、可以用递归将复杂任务分解成更简单的子问题;
3、使用递归比使用一些嵌套迭代更容易。
缺点:
1、递归的逻辑很难调试、跟进;
2、递归调用的代价高昂(效率低),因为占用大量的内存和时间
下面根据一个实例来详细讲解递归数列
使用递归数列来实现获取第n个斐波那契数字:
1 1 2 3 5 8 13 21 ……
我们先观察数列的规律
不难发现 第n个数是前两个数字之和
def hinami(n):
if n ==1 or n == 2:
return 1
else:
return hinami(n-2) + hinami(n-1)
print(hinami(5)) #结果为5
当n=5时,为了更直观的感受,我们来罗列一下数列
5 = hinami(4) + hinami(3)
hinami(3) + hinami(2) + hinami(2) + hinami(1)
hinami(2) + hinami(1) + hinami(2) + hinami(2) + hinami(1)
1 + 1 + 1 + 1 + 1 = 5
上面提到 随着递归深度的增加,需要维护很大的函数调用栈,开销太大 所以不建议使用
这里举个例子来具体演示一下
在Python中 默认递归深度为999,当超过这个深度时系统就会报错
如果我们一定要使用递归数列,那么如何解决这个深度限制呢?
这里我们插入sys
import sys
sys.getrecursionlimit(),'默认最大递归深度'
sys.getrecursionlimit(),'设置最大递归深度'
使用以上方法就可以解决最大递归深度问题啦
8.匿名函数
匿名函数基础
首先我们了解匿名函数的格式
lambda argument1,argument2,argument3 : epression #前面是形参接受我们传入的实参,
#冒号后面是函数体
squre = lambda x : x**2
squre(3) #输出9
上面的函数其实就相当于
def squre(x):
s = x ** 2
return s
lambda是一个表达式,并不是一个语句
通常我们用匿名函数的目的就是以下几点:
一、减少代码的重复性,
二、模块化代码。
一个函数,但它非常简短,只需要一行就能完成,
同时它在程序中只被调用一次。那么这种情况,
就可以是匿名的,只需要在适当的地方定义并使用,
从而发挥匿名函数的作用。
9.装饰器
目的
不改变函数原有实现,给函数添加新功能
本质
闭包
1.外部函数嵌套内部函数
2.外部函数把内部函数返回
3.内部函数获取外部函数局部变量
@语法糖
#定义一个装饰器
def login_check(f): #2外部函数返回 内部函数check(返回的是个函数,但是没有调用)
def check():
user = input("输入用户名")
psd = input('请输入密码')
if user == 'a' and psd == '123'
f()
else:
print('输入错误')
return check #注意这里返回不要加() 加括号之后函数就被调用了
method1 = login_check(sys_shop) #1调用验证方法,传入实参 #3把check赋值给method1
method1() #4 method1() 加小括号调用check方法,此时check中的f是我们
#传入的实参sys_shop
def sys_shop():
print('欢迎进入购物系统')
上面就是一个简易的装饰器,我们调用装饰器时每次都要输入
method1 = login_check(sys_shop)
method1()
可以使用下面这个方法来代替上面两行
@login_check #这就相当于上面的这两行代码
def sys_shop()
print('欢迎进入购物系统') #当系统读取到@时会自动认为下面是被包装的函数