一、装饰器
#1.带有参数的装饰器 def getAge1(age): print(age) def outter1(func): def inner1(n): if n < 0: n = abs(n) func(n) return inner1 f1 = outter1(getAge1) #func = getAge f1(-5) #f1---》inner print("=" * 30) #2.使用 @xxx 简化装饰器的使用过程,xxx表示装饰器的名称【外部函数的函数名】 #注意:只需要将@xxx添加在需要被装饰的函数的前面 def outter2(func): print("11111111") def inner2(n): print("2222222") if n < 0: n = abs(n) func(n) print("33333333") return inner2 @outter2 #相当于调用了装饰器的外部函数 def getAge2(age): print("4444444") print(age) """ @outter2的工作原理: 调用outter2,并将原函数传参:getAge2 ----》func 将装饰器内部函数返回,并getAge2的指向发生了改变,getAge2 ---->inner2 getAge2 = outter2(getAge2) """ getAge2(-17) #练习 def check_time(action): def do_action(x,y): #新功能 print("十点了") #调用原函数 action(x,y) return do_action @check_time #调用外部函数,完成了传参和返回值 def go_to_bed(a,b): print("{}去{}睡觉".format(a,b)) go_to_bed("张三","卧室") #3.带有返回值的装饰器 #注意:不管是闭包还是一个装饰器,任意函数和中的参数或者返回值和普通函数的用法完全相同 def wrapper(fun): def inner(): print("new~~~") fun() return "aaaaaaaa" return inner @wrapper def test(): print("test~~~~") result = test() print(result) print("=" * 30) #4.同一个装饰器修饰不同的函数 #注意:如果需要同一个装饰器给不同的函数增加相同的功能, # 为了满足不同函数的需求,尽量将装饰器内部函数的参数设置为不定长参数,格式:*args,**kwargs #说明:*args,**kwargs中的args和kwargs可以是任意的变量 def wrapper1(func): def inner1(*args,**kwargs): print("11111") func(*args,**kwargs) return inner1 @wrapper1 def check1(): print("check~~~1111") @wrapper1 def check2(a,b): print("check~~~2222",a,b) @wrapper1 def check3(name,age,hobby): print("check~~~3333",name,age,hobby) check1() check2(34,10) check3("jack",10,"唱歌")
二、生成器、可迭代对象和迭代器
1.生成器
通过列表推导式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator
定义生成器的两种方式
第一种:列表推导式的中括号改为小括号,就是一个生成器 第二种:通过函数和yield关键字生成
#1.列表推导式[]--->() l1 = [i * 2 for i in range(5)] print(l1,type(l1)) #<class 'list'> g = (i * 2 for i in range(5)) print(g,type(g)) #<generator object <genexpr> at 0x10ed609a8> <class 'generator'>g #注意:生成器也是一种容器,可以通过for循环获取其中的元素,还可以使用 next()获取生成器中的下一个元素 #Iterable:可迭代对象 Iterator:迭代器 #print(next(l1)) #TypeError: 'list' object is not an iterator print(next(g)) print(next(g)) print(next(g)) print(next(g)) print(next(g)) #注意:如果一个生成器中的元素已经获取完毕,那么继续使用next则会报错StopIteration #print(next(g)) #StopIteration停止迭代 print("=" * 10) #2.函数结合yield实现 def test1(): for x in range(5): print(x) t1 = test1() print(t1) print("=" * 10) #注意1:如果在一个函数的内部使用了yield关键字,那么该函数将是一个生成器 def test2(): for x in range(5): print("hello",x) yield x * 2 print("循环内部:",x) t2 = test2() print("2222",t2) print(next(t2)) #0 print(next(t2)) #循环内部: 0 2 print(next(t2)) print(next(t2)) print(next(t2)) #print(next(t2)) """ 注意: a.在函数中,yield xxx, xxx表示生成器中的元素,yield在函数中出现几次, 则意味着生成器中的元素有几个 b.yield的作用: 保存当前的运行状态,然后暂停执行 将yield之后的数据作为返回值返回 c.可以使用next()让生成器从暂停的位置开始继续执行,唤醒生成器【函数】 """ print("=" * 30) #练习 def show(n): yield n ** 3 result = show(4) print(result) print("元素:",next(result))
2.可迭代对象【Iterable】和迭代器
""" 【面试题】 区别 可迭代对象:只能使用for循环遍历的对象 a.一类是集合数据类型:如list,tuple,str,dict,set b.generator:()和函数 迭代器:不但可以使用for循环遍历,还可以使用next函数获取元素的对象 目前只有生成器 联系: 如果一个数据是可迭代对象,不一定是迭代器 如果是迭代器,则一定是可迭代对象 可以通过iter()将不是迭代器的可迭代对象转化为迭代器 """ from collections import Iterable,Iterator #isinstance(数据,类型),判断指定数据是否是指定的类型 print(isinstance([],Iterable)) print(isinstance({},Iterable)) print(isinstance((x for x in range(5)),Iterable)) print("**********") print(isinstance([],Iterator)) print(isinstance({},Iterator)) print(isinstance((x for x in range(5)),Iterator)) print(isinstance(iter([]),Iterator)) print(isinstance(iter({}),Iterator))
三、函数递归
递归函数:一个会调用自身的函数被称为递归函数
递归调用:一个函数,调用自身,称为递归调用
递归包含了一种隐式的循环,它会重复执行某段,但这种重复无须循环控制【凡是循环能干的事,递归都能搞定】
使用递归解决问题的思路使用递归解决问题思路:
a.找到一个临界条件【临界值】
b.找到相邻两次循环之间的关系
c.一般情况下,会找到一个规律【公式】
#1.函数自己调用自己,称为递归 # def test(): # print("hello") # test() # # test() #RecursionError: maximum recursion depth exceeded while calling a Python object #2.需求1:封装一个函数,传入一个数,返回对应的数,如:10---》55,7---》13 """ 斐波那契数列 1 2 3 4 5 6 7 8 9 10 11 1 1 2 3 5 8 13 21 34 55 89.。。。 分析:fun(n) fun(1) --->1 fun(2) ---->1 fun(3)--->fun(2) + fun(1) fun(4)--->fun(3) + fun(2) .... fun(10)---->fun(9) + fun(8) ... fun(n)---->fun(n - 1) + fun(n - 2) """ count = 0 def fun(n): #统计函数执行的次数 global count count += 1 if n == 1 or n == 2: return 1 else: return fun(n - 1) + fun(n - 2) print(fun(10)) #10--->55 # print(fun(1)) #1 # print(fun(2)) #1 print(count) #3.需求2:用函数递归的思想求1~某个数之间所有整数的和 """ getsum(1)---->1 getsum(2)--->getsum(1) + 2 getsum(3) ---->getsum(2) + 3 ..... getsum(50)---->getsum(49) + 50 ... getsum(n)---->getsum(n - 1) + n """ def getsum(n): if n == 1: return 1 else: return getsum(n - 1) + n print(getsum(100)) #5050
四、包和模块
1.包
包是一种管理 Python 模块命名空间的形式,采用"点模块名称"
就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况
Python package本质是一个文件夹【目录】,但是特殊之处在于在改目录下有一个文件,名为__init__.py,代表初始化,前期其中不写任何内容,后期会在其中书写项目的配置信息 注意:一个py文件实际上就是一个模块
2.模块
2.1自定义模块
#一、import xxx #说明:xxx表示需要被导入的模块的路径,语法:点语法【模块的层级关系】,import表示将指定的文件加载 #1.import xxx,缺点在于:当模块的层级关闭比较复杂时,则调用函数比较麻烦 #注意:路径使用的是相对路径,默认参照的路径是当前工程的根目录 # import aaa.test # import bbb.test # aaa.test.func() # bbb.test.func() #2.as可以给比较复杂的路径起一个别名,然后通过别名调用函数 import aaa.test as a import bbb.test as b a.func() b.func() #注意:通过import xxx方式导入模块,调用函数默认从当前文件查找 def func(): print("ccccc") func() def func(): print("ccccc~~~~22222") func()
#二、from xxx1 import xxx2 #说明:xxx表示模块的路径,xxx2表示需要导入的内容【变量,函数,类】 #1.注意:from xxx1 import xxx2,可以简化函数的调用,将不再指明函数的来源, # 但是缺点:当不同模块中出现同名的函数,后导入的模块中的函数会覆盖先导入的 """ from bbb.test import func1,func2 from aaa.test import func1,func2 func1() func2() def func1(): print("当前文件") func1() """ #2.from xxx1 import *,*代表所有 from aaa.test import * func1() func2() """ 建议from ...import """