9.28总结
知识回顾
# 1. 封装一个函数:获取指定数据的阶乘 【没有指定数据的话默认求10的阶乘】 默认参数# 阶乘 比如5!=5*4*3*2*1# 未知数据 有1个# 是否需要返回结果deffactorial(num=10):result=1foriinrange(num,0,-1):result*=ireturnresultprint(factorial(7))print(factorial())# 2. 封装一个函数:求多个数据中的偶数之和 [可变参数]defget_sum(*nums):total=0foreleinnums:ifele%2==0:total+=elereturntotalprint(get_sum(18,27,35,46))# 3. 设计一个函数:功能是进行自我介绍, 但是不同的人自我介绍的信息不同 信息传递时需要指明信息标题与信息值# 比如 姓名=小红defintroduce(**infos):print(infos)# 字典类型容器数据 存储的键值对数据 信息绑定的数据 标题和值绑定在一起introduce(姓名='小明',年龄=18,毕业院校='加里敦大学')# 关键字参数传值introduce(姓名='小红',出生年份=2000,毕业院校='加里敦大学',专业='挖煤')# 命名关键字参数 定义函数的时候 参数是定义在*号后面 传值的时候必须使用关键字形式传值defregister(*,username,password):print(username,password)register(username='admin',password='123456')2.7.3 递归算法
算法:前辈们总结出来的解决某种问题的方法
递归:函数自己内部调用自己的结构称为递归,递归是循环的另外一种表达形式
能形成递归代码的问题都是有规律的,可以找到规律,把规律映射成函数
斐波拉契数列:求第N个月的兔子的对数
公式: F(1)=1F(2)=1F(n)=F(n-1)+F(n-2)(n>=3)F可以映射成函数名,小括号中的n映射成形参
注意:
- 写递归的时候一定要有已知项【代表了递归的出口】,没有这个已知项相当于形成了死循环
- 递归的层次不能太深 【递归就是在重复的调用自己 就是一个循环 可能会造成程序卡顿 或者 程序崩溃】
# 求第N个月兔子的对数""" F(1) = 1 F(2) = 1 F(n) = F(n-1) + F(n-2) (n>=3) """defF(n):ifn==1orn==2:return1else:returnF(n-1)+F(n-2)# F(2) + F(1) # 1 + 1# 调用函数print(F(1))print(F(2))print(F(3))""" 求1-N的累加和 F(1) = 1 F(2) = 1 + 2 = F(1) + 2 F(3) = 1 + 2 + 3 = F(2) + 3 .... F(n) = F(n-1) + n n>=2 """defget_sum(n):ifn==1:return1else:returnget_sum(n-1)+nprint(get_sum(10))""" 数字 不要使用字符串 传递数据的位数 呈现指定的数据 位数为1位数 呈现的2 为2位数 呈现的22 为3位数 呈现的222 第一种 F(1) = 2 F(2) = 22 = F(1) * 10 + 2 F(3) = 222 = F(2) * 10 + 2 .... F(n) = F(n-1) * 10 + 2 第二种 F(1) = 2 F(2) = 22 = 2*10 + F(1) = 2*10**(2-1) + F(1) F(3) = 222 = 2*100 + F(2) = 2*10**(3-1) + F(2) F(4) = 2222 = 2*1000 + F(3) = 2*10**(4-1) + F(3) .... F(n) = 2 * 10 **(n-1) + F(n-1) """defget_num(n):ifn==1:return2else:returnget_num(n-1)*10+2defget_num1(n):ifn==1:return2else:return2*10**(n-1)+get_num1(n-1)# 面试题:""" 吃豆子,一次只能吃1颗或者2颗,但是不能连续两次吃2颗, 问N颗豆子的吃完的方法有多少种 1颗 1种 2颗 2种 3颗 3种 1颗的吃 21 12 4颗 4种 1111 112 121 211 5颗 6种 11111 1112 1121 1211 2111 212 6颗 9种 111111 11112 11121 11211 12111 21111 1212 2121 2112 7颗 13种 1111111 111112 111121 111211 112111 121111 211111 11212 12112 12121 21112 21121 21211 F(n) = F(n-1) + F(n-3) n>=4 已知项是3个值 """defeat_num(n):ifn<=3:returnnelse:returneat_num(n-1)+eat_num(n-3)2.7.4 函数嵌套
全局变量与局部变量
作用域:限定标识符可用性的使用的范围,就称为作用域
在一般的变成语言中代码块一般都是单独的作用域,比如分支结构/循环结构/函数,但是在Python中只有函数有单独的作用域,这个作用域限定了在函数定义的变量只能在函数中使用,这个变量称为局部变量【换句话说这个变量只能应用于函数体内,出了函数没有意义】
注意:形式参数也是局部变量,随着函数的定义才出现的
defadd(a,b):returna+bprint(add(a=18,b=22))# 在函数外部 打印a和b的值# print(a, b) # NameError: name 'a' is not defined # a是未定义的# 出了函数就没意义了全局变量:定义的这个变量可以在任意位置使用,称为全局变量
name='小红'# 函数外defshow_name():print(name)# 调用函数show_name()# 定义一个函数:修改名称defupdate_name():name='小丽'# 如果没有这个标记,解释器会认为这个变量是在函数内部重新定义的,与全局没有关系update_name()print(name)# 小红# 修改全局的名称defupdate_name1():globalname# 告诉解释器 将使用的这个name是全局变量name='小丽'update_name1()print(name)# 小丽介绍一个修饰符:global
使用场景:要在函数中修改全局变量的值时,需要在函数中先对变量进行标记[将其标记为全局变量], 如果没有这个标记,解释器会认为这个变量是在函数内部重新定义的,与全局没有关系
内存结构
内存也是分为不同的区域的,不同的区域存储的数据是不一样的,数据的生命周期也是不一样的,区域有:
栈区
调用的函数存储与栈区,栈区有一个存储特点:先进后出【类似于向上开口的容器】,函数运行完会被立即释放
堆区
常量池
常量池中存储的是数值类型以及字符串类型的数据
特点:获取数据时检查数据是否在常量池中存在,如果存在的话直接根据地址获取数据,如果不存在,先分配空间添加数据,再把数据的地址赋值给变量
生命周期:当程序结束的时候才会释放
静态池
存储全局变量【直接在
py文件中定义的变量称为全局变量】生命周期:当程序结束的时候才会释放
方法区
定义的方法或者类会存储于方法区中
1.函数嵌套【了解】
在一个函数中定义了另外一个函数,这种格式称为函数嵌套
了解的内容是:【闭包/装饰器】
闭包: 在函数嵌套 内层函数应用了外层函数的变量,这种结构就称为闭包# 函数嵌套格式defouter():print('外层函数')definner():print('内部函数')# 了解的是如何调用到内层函数outer()# 调用外层函数# 能直接写inner()?? 为什么不能?? 函数有自己作用域 在函数中内部定义的内容只能在函数内部使用 外部无法直接拿到# inner() # 不能这样调用内层函数# 不能直接调用 只能间接调用""" 格式1: 在外层函数中 定义完即运行 """defouter1():print('外层函数1')definner1():print('内部函数1')# 调用inner1()outer1()""" 格式2: 不让其直接运行 向手动调用 把内部函数当做数据值返回 注意:返回的时候不要加() 因为 函数名() 代表的是调用函数 运行函数内部机制 此时返回不是函数本身 而是函数的运行结果 """defouter2():print('外层函数1')definner2():print('内部函数1')# 返回returninner2 res3=outer2()print(res3)# <function outer2.<locals>.inner2 at 0x000002217D7814C0>res3()- 加不加括号的区别:
--1.不加括号defadd(a,b):returna+b res=add# add是一个函数名 代表求和的功能 它是一个函数print(res)# <function add at 0x00000116E52B50D0>print(type(res))# <class 'function'>--1.加括号defadd(a,b):returna+b res1=add(12,14)# 运行内部机制,计算函数功能的结果 赋值给res1print(res1)# 26print(type(res1))# <class 'int'>2. nonlocal
""" 在内部函数中修改外部函数中的局部变量时 会使用nonlocal """defouter4():num=10definner4():nonlocalnum num=100print('内层函数:',num)inner4()print('外层函数的num值',num)outer4()# 内层函数: 100 外层函数的num值 1003. 装饰器:
# 装饰器:特殊的闭包""" 除了在内层函数中使用了外层函数的变量外,外层函数的返回值是内层函数 """""" 装饰器使用来装饰函数或者类的,目的是在执行功能的时候 除了执行功能本身之外 还增加了额外的功能 """# 需求:调用函数的时候 先验证传递到函数中的数据是否是整数类型的 是整数类型的让其参与运算 否则直接报错# 求和的功能defget_sum(a,b):iftype(a)!=intortype(b)!=int:raiseTypeError('excepted int')returna+b# 求差的功能defget_sub(a,b):iftype(a)!=intortype(b)!=int:raiseTypeError('excepted int')returna-b""" 上面代码中 判断数据是否是整数类型的操作是被重复书写的 为了简化代码:会把重复的功能封装成函数 """defcheck_type(a,b):iftype(a)!=intortype(b)!=int:raiseTypeError('excepted int')returnTruedefget_mul(a,b):result=check_type(a,b)ifresult:returna*bprint(get_mul(12,34))# 408print(get_mul(12,3.4))# 会报错# 一开始把各种运算功能全部写完了defdiv(a,b):returna/bdefmod(a,b):returna%b# 产品经理:函数执行的时候 验证数据是否是整数类型的 【有没有不修改写好的功能 直接增加类型判断的操作???】# 装饰器 相对于上面来说会更加简洁一些 不用在动写好的功能了# 将写好的装饰器使用语法糖的形式 给需要增加类型判断的函数 装饰一下# @语法糖 形式