首页 > 学院 > 操作系统 > 正文

Python-day4

2024-06-28 16:00:47
字体:
来源:转载
供稿:网友

1.装饰器

解:本质上是函数,在语法上和普通的函没有区别(装饰其他函数,为其他函数添加附加功能) 原则1:不能修改被装饰的函数的源代码 原则2:不能修改被装饰的函数的调用方式 注:对于被装饰器装饰的函数而言,装饰器的透明,完全感知不到其的存在

装饰器知识储备: 1.函数即“变量” 2.高阶函数 3.嵌套函数

高阶函数+嵌套函数====》》》》装饰器

函数的理解: 注:其实在Python中,函数也是“变量”,在计算机中,函数就是将函数体(即函数内容)赋值给一个名叫xxx(函数名字)的变量,然后引用的时候xxx(),变量则是直接引用变量名字就可以了,其中函数体在内存中存在形式只是一堆字符串,而且如果变量x=1,在内存中会将1这个值实实在在的存放下来,如果又有y=x,那么在内存中1的值将被二次引用,只有当x,y都不存在了那么1占用的地址空间才会被内存释放,Python的内存回收机制就是这种原理

1.函数讲解1:

def one(): PRint("This is one scripts!") two()def two(): print("This is two scripts!")one()###################################This is one scripts!This is two scripts!def two(): print("This is two scripts!")def one(): print("This is one scripts!") two()one()########################################This is one scripts!This is two scripts!

注:注意以上两端函数的定义以及引用,其实没有本质上的区别,为什么呢?因为每当Python读取函数函数对应的函数体时,这时内存已经记录了这段函数体的地址,如果发现函数体里面也引用了函数,那么就会去查这个函数对应的函数体有没有存在于内存中,如果有就去查相应的“门牌号”——函数名字,注意仅仅是在函数定义时函数嵌套函数的后者定义的顺序是没有限制的,只要存在那么Python就可以找得到被嵌套函数的函数体存在于内存何处以及相应的“门牌号”——函数名字

高阶函数

1.把一个函数名当做实参传递给另外一个函数

2.返回值包含函数名

例1:

#!/usr/bin/env python3# this is tandabin scripts!!def test(here): print(here) here() org()def org(): print("asd")test(org)#########################################<function org at 0x0000000000B3E840>asdasd#############################################org=here,门牌号相同,相当于org()的函数体也同时赋值,所以诞生了这么个引用here()

注:当高阶函数的参数赋值被一个函数名赋值,那么这个参数本身就已经是个函数了,所以会出现以上的结果,记住不管是here,还是org都只是“函数名”,记录在内存中的内存地址

例2:

#!/usr/bin/env python3# this is tandabin scripts!!import timedef test(here): start_time = time.time() here() stop_time = time.time() print("Script time %s"% (stop_time-start_time))def org(): time.sleep(3) print("asd")test(org)

注:以上的做法就有点类似装饰器的概念,可以通过在装饰器函数体中加入定时的内容去计算被装饰函数的运行时间,满足装饰器条件1中的“不修改原函数的源代码”,但是会发现调用的方式变了,不符合装饰器条件2中的“不修改调用方式”

例3:

#!/usr/bin/env python3# this is tandabin scripts!!import timedef org(): time.sleep(3) print("in the org")def test(here): print(here) return hereorg = test(org)org()

注:以上是根据返回值调用函数实现装饰器的做法,而且解决了在既不修改函数源代码的情况下还实现了不修改函数的调用方式就能起到装饰器的作用,呈现函数原有的功能并且添加新的输出

例4:

#!/usr/bin/env python3# this is tandabin scripts!!import timedef one(): print("one") def two(): print("two") two()one()#############################onetwo

注:函数中嵌套声明函数,这叫嵌套函数,但是被嵌套的函数相当于局部变量,不可以在外界进行调用,只能被主函数进行调用

例5:

#!/usr/bin/env python3# this is tandabin scripts!!import timedef timer(func): def deco(): start_time = time.time() func() stop_time = time.time() print("Func time: %s"% (stop_time-start_time)) return decodef test1(): time.sleep(3) print("test1")def test2(): time.sleep(3) print("test2")test1 = timer(test1)test1()test2 = timer(test2)test2()################################test1Func time: 3.000171661376953test2Func time: 3.000171422958374

注:高阶函数+嵌套函数+不修改函数源代码+不修改调用方式完成装饰器功能,其中装饰器函数是将timer是将test1,test2作为参数传入,然后执行装饰器函数时往下执行嵌套函数deco,deco函数中的功能是计算func函数的执行时间(func是作为参数传入的,由于test1,test2的传入,所以func=test1,func=test2,而且函数名其实在内存中就是变量,故而可以通过func()方式调用函数体),当deco函数执行完后,返回上一次发现timer函数的执行语句其实就是return返回deco函数的返回结果,也就是对应的内存地址(门牌号),然后调用的时候通过将timer(test1)的结果(因为timer函数执行完就只是返回deco的内存地址)赋值给test1函数变量名,修改test1的函数体,然后test1()调用,就可以做到装饰器的作用,一来正常执行test1的函数,而来增加了计算test1函数的执行时间

例6:
#!/usr/bin/env python3# this is tandabin scripts!!import timedef timer(func): def deco(): start_time = time.time() func() stop_time = time.time() print("Func time: %s"% (stop_time-start_time)) return deco@timerdef test1(): time.sleep(3) print("test1")@timerdef test2(): time.sleep(3) print("test2")test1()test2()

注:@timer的作用是替代上文的test1 = timer(test1)

例7:

#!/usr/bin/env python3# this is tandabin scripts!!import timedef timer(func): def deco(*args,**kwargs): start_time = time.time() func(*args,**kwargs) stop_time = time.time() print("Func time: %s"% (stop_time-start_time)) return deco@timer# 等于test1 = timer(test1) = deco 由于装饰器函数timer的函数返回结果deco的内存地址(“门牌号”)def test1(): time.sleep(3) print("test1")@timer# 等于test2 = timer(test2) = decodef test2(name,age): time.sleep(3) print("test2 and %s and %s"% (name,age))test1()test2("charlie",21)

注:如果在test2中想引用参数,只需要在装饰器函数中的嵌套函数deco中加入形参以及func参数加入形参就可以实现,由于装饰器函数的返回结果就是deco(deco函数的函数名,即内存地址),所以test2()等于deco(),test2加入参数也只需要在deco中加入参数即可,如上诉例子,在装饰器引入参数并不会影响test1函数的调用,可以调入参数也可以不调入参数,用*ages以及**kwargs就可以实现

例8:

#!/usr/bin/env python3# this is tandabin scripts!!import os,getpassusername = "charlie"passWord = "abc123"def first(func): def verify(*args,**kwargs): fulluser = input("Please input your user: ") fullpassword = input("Please input your pass: ") if fulluser == username and fullpassword == password: print("verify successfully!!") func(*args,**kwargs) else: exit("Input invalid!") return verifydef index(): print("welcome to index!!!")@firstdef home(): print("welcome to home!!!")@firstdef second(): print("welcome to second!!!")index()home()second()############################################welcome to index!!!Please input your user: charliePlease input your pass: abc123verify successfully!!welcome to home!!!Please input your user: charliePlease input your pass: abc123verify successfully!!welcome to second!!!############################################welcome to index!!!Please input your user: charliePlease input your pass: abc123verify successfully!!welcome to home!!!Please input your user: charasPlease input your pass: 123Input invalid!

注:常见的网站次级页面登录模拟装饰器的应用场景

装饰器范例终极版:

#!/usr/bin/env python3# this is tandabin scripts!!username = "charlie"password = "abc123"def first(verify_type): print("verify type: ",verify_type) def twice(func): def verify(*args,**kwargs): print("verify type: ",*args,**kwargs) fulluser = input("Please input your user: ") fullpassword = input("Please input your pass: ") if verify_type == "local": if fulluser == username and fullpassword == password: print("verify successfully!!") print(*args,**kwargs) res = func(*args,**kwargs) print(res) else: exit("Input invalid!") elif verify_type == "ldap": print("without ldap!!!") return verify return twicedef index(): print("welcome to index!!!")@first(verify_type = "local")def home(*args,**kwargs): print("welcome to home!!!") return "from home"@first(verify_type = "ldap")def second(): print("welcome to second!!!")index()home("CR",7,"9400W","the best man")second()#################################################verify type: localverify type: ldapwelcome to index!!!verify type: CR 7 9400W the best manPlease input your user: charliePlease input your pass: abc123verify successfully!!CR 7 9400W the best manwelcome to home!!!from homeverify type: Please input your user: charliePlease input your pass: abc123without ldap!!!

注:通过不同验证方式开启不用的校验登录到不同页面,三级嵌套函数的装饰器可以实现这一个功能,通过将装饰器以函数的形式调用,并且往里面加入参数,然后将装饰器装饰的函数时本来就有的一个参数func通过往下压一层到二级嵌套函数twice中导入func,然后接下来的引用就和上文一样,通过“偷梁换柱”将原本外界调用的主函数以装饰器函数中对verify的调用而替换,并且加入if、else的判断从而可以实现功能新添,而且外界调用函数时还可以添加不限制个数的参数到主函数中去掉用,然后被加入的函数又有同时在装饰器中被引用,如果不添加参数也不影响实际的用户体验效果

2.列表生成式:

print([ i*2 for i in range(10)])#####################################[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

注:节省代码,快速生成列表

3.生成器:

注:通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

3.1.范例1:

b = (i*2 for i in range(10))print(b)##############################<generator object <genexpr> at 0x00000000007C69E8>

注:生成器最基本用法

3.2.范例2:

b = (i*2 for i in range(10))for i in b: print(i)#############################024681012141618

注:利用生成器的方法生成100W个元素的效率远比定义100W个元素的速度快

3.3范例3:

b = [i*2 for i in range(10000000)]print(b)#######################################略........................2, 3860354, 3860356, 3860358, 3860360, 3860362, 3860364, 3860366, 3860368, 3860370, 3860372, 3860374, 3860376, 3860378, 3860380, 3860382, 3860384, 3860386, 3860388, 3860390, 3860392, 3860394, 3860396, 3860398, 3860400, 3860402, 3860404, 3860406, 3860408, 3860410, 3860412, 3860414, 3860416, 3860418, 3860420, 3860422, 3860424, 3860426, 3860428, 3860430, 3860432, 3860434, 3860436, 3860438, 3860440, 3860442, 3860444, 3860446, 3860448, 3860450, 3860452, 3860454, 3860456, 3860458, 3860460, 3860462, 3860464, 3860466, 3860468, 3860470, 3860472, 3860474, 3860476, 3860478, 3860480, 3860482, 3860484, 3860486, 3860488, 3860490, 3860492, 3860494, 3860496, 3860498, 3860500, 3860502, 3860504, 3860506, 3860508, 3860510, 3860512, 3860514, 3860516, 3860518, 3860520, 3860522, 3860524, 3860526, 3860528, 3860530, 3860532, 3860534, 3860536, 3860538, 3860540, 3860542, 3860544, 3860546, 3860548, 3860550, 3860552, 3860554, 3860556, 3860558, 3860560, 3860562, 3860564, 3860566, 3860568, 3860570, 3860572, 3860574, 3860576, 3860578, 3860580, 3860582, 3860584, 3860586, 3860588, 3860590, 3860592, 3860594, 3860596, 3860598, 3860600, 3860602, 3860604, 3860606, 3860608, 3860610, 3860612, 3860614, 3860616, 3860618, 3860620, 3860622, 3860624, 3860626, 3860628, 3860630, 3860632, 3860634, 3860636, 3860638, 3860640, 3860642, 3860644, 3860646, 3860648, 3860650, 3860652, 3860654, 3860656, 3860658, 3860660, 3860662, 3860664, 3860666, 3860668, 3860670, 3860672, 3860674, 3860676, 3860678, 3860680, 3860682, 3860684, 3860686, 3860688, 3860690, 3860692, 3860694, 3860696, 3860698, 3860700, 3860702, 3860704, 3860706, 3860708, 3860710, 3860712, 3860714, 3860716, 3860718, 3860720, 3860722, 3860724, 3860726, 3860728, 3860730, 3860732, 3860734, 3860736, 3860738, 3860740, 3860742, 3860744, 3860746, 3860748, 3860750, 3860752, 3860754#略.............c = (i*2 for i in range(10000000))for i in c: print(i)#######################################略................296428296430296432296434296436296438296440296442296444296446296448296450296452296454(Ctrl C取消)..............

注:如上图所示,使用生成器的效果是列表的生成读取以及内存占用只会到循环的最后一次,什么时候Ctrl C就只是占用从开始到中断的这部分的内存空间,如果是使用列表的定义,不管循环到第几层,内存处理数据都会读取整个列表的数据以及每个数据占用一个内存空间,然后才做逻辑处理,两种方式体现了内存的优化差异性

注:生成器只有在调用时才会生成相应的数据,并且只记录当前的值,循环过的前面的值生成器是不会记录的,换言之就是循环过的就“没”了,只可以继续往后循环,不可以回退,而且不能跨步循环,只能一个个的往下一个值循环,这样的目的是为了最大程度的节省内存。

3.4函数生成器斐波那契:

def fib(max): a = 0 b = 1 count = 0 while count < max: print(b) a = b b = a + b count+=1 return 'False!'fib(10)##################################1248163264128256512#################################################def fib(max): a = 0 b = 1 count = 0 while count<max: print(b) a,b = b,a+b count+=1 return 'False'fib(10)#####################################################11235813213455

注:注意两种函数写法的不同输出结果,下面一种的赋值方式相当于; t = (b, a + b) # t是一个tuple a = t[0] b = t[1] 值不会随着上面方式改变而改变,这里避免混淆!

3.5函数生成器真正实现方式:

#!/usr/bin/env python3# this is tandabin scripts!!def fib(max): a = 0 b = 1 count = 0 while count<max: yield b a,b = b,a+b count+=1 return 'False'f = fib(10)print(f.__next__())print(f.__next__())print("CR7".center(50,"="))print(f.__next__())print(f.__next__())print("start for".center(50,"="))for i in f: print(i)###############################################11=======================CR7========================23====================start for=====================5813213455

注:将print换成yield就是函数变成生成器的做法,而且生成器的好处就是可以随时控制循环到某个特定位置时跳出来去执行其他的动作,执行完之后再回去继续后面的循环!!!

3.6函数生成器真正实现方式2:

#!/usr/bin/env python3# this is tandabin scripts!!def fib(max): a = 0 b = 1 count = 0 while count<max: yield b a,b = b,a+b count+=1 return 'Are you kidding me?'f = fib(6)while True: try: x = next(f) print('f:',x) except StopIteration as e: print('StopIteration return value:',e.value) break#######################################################f: 1f: 1f: 2f: 3f: 5f: 8StopIteration return value: Are you kidding me?

注:当通过for循环输出生成器的时候,return结果是没有的,不要问我为什么,但是当用while循环的时候,就可以利用StopIteration报错的方式检测,当时错误的时候抓取关键字然后让循环结束,也就是说函数生成器的return相当于是拿来排错的!

3.7简单协程用法1:

import timedef consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name))f = consumer("charlie")f.__next__()f.__next__()f.send("猪肉包子")#############################################################charlie 准备吃包子啦!包子[None]来了,被[charlie]吃了!包子[猪肉包子]来了,被[charlie]吃了!

注:next方法和send方法唯一的区别是,都是调用yield并且返回结果,但是send会将值传入yield再返回,而next方法只调用yield并不会传值返回

3.8多任务并行执行:

import timedef consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name))def producer(name): c = consumer('charlie') c2 = consumer('bob') c.__next__() c2.__next__() print("老子开始准备做包子啦!") for i in range(5): time.sleep(1) print("做了2个包子!") c.send(i) c2.send(i)producer("CR7")#############################################################charlie 准备吃包子啦!bob 准备吃包子啦!老子开始准备做包子啦!做了2个包子!包子[0]来了,被[charlie]吃了!包子[0]来了,被[bob]吃了!做了2个包子!包子[1]来了,被[charlie]吃了!包子[1]来了,被[bob]吃了!做了2个包子!包子[2]来了,被[charlie]吃了!包子[2]来了,被[bob]吃了!做了2个包子!包子[3]来了,被[charlie]吃了!包子[3]来了,被[bob]吃了!做了2个包子!包子[4]来了,被[charlie]吃了!包子[4]来了,被[bob]吃了!

注:nginx之所以速度快就是因为采用了以上的单线程多协程的工作原理,提高执行效率

迭代器:

我们知道,可以直接作用于for循环的数据类型有以下几种:

一类是集合数据类型,如list、tuple、dict、set、str等等;

一类是generator,包括生成器和带yield的generator function

这些可以直接作用于for循环对象称为可迭代对象:iterable

可以使用isinstance()判断一个对象是否是iterable对象:

3.9判断数据类型是否是可迭代对象:

>>> isinstance([],Iterable)True>>> isinstance({},Iterable)True>>> isinstance((x for x in range(10)),Iterable)True>>> isinstance('abc',Iterable)True>>> isinstance(100,Iterable)False

注:而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了

*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

:值得注意,生成器都是迭代器对象,但是可迭代对象不一定是迭代器

3.10判断a有哪些方法可以调用:

>>> a = [1,2,3]>>> dir(a)['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']>>>

3.11利用iter方法将列表转换成迭代器:

>>> a = [1,2,3]>>> a[1, 2, 3]>>> iter(a)<list_iterator object at 0x7f05445f9ba8>>>> p = iter(a)>>> p.__next__()1>>> p.__next__()2>>> p.__next__()3>>> p.__next__()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration

注:把list、dict、str等Iterable变成Iterator可以使用iter()函数

你可能会问,为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

小结

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

Python的for循环本质上就是通过不断调用next()函数实现的,例如:

4.1内置函数all

print(all([1,2,3]))print(all([0,2,3]))#############################TrueFalse

注:利用内置函数all判断一个可迭代对象是否为“真”,值得注意的是在计算机中“非”“0”即为“真”

4.2内置函数any

print(any([0,1,0]))print(any([]))###########################TrueFalse

注:和all的区别是,如果判断的对象是有一个可迭代对象,那么就会返回“真”

4.3内置函数bin

>>> bin(4)'0b100'>>> bin(255)'0b11111111'>>> bin(223)'0b11011111'

注:数字的二进制转换

4.4内置函数bool

>>> bool<class 'bool'>>>> bool(0)False>>> bool(1)True>>> bool([1,2,3])True>>> bool([])False

注:判断真假

4.5内置函数callable

def a(): print("Yes")print(callable([]))print(callable(a))######################FalseTrue

注:判断一个对象是否可以被调用,即加上()

4.6内置函数chr,ord

>>> chr(123)'{'>>> chr(100)'d'>>> ord("b")98>>> ord("d")100>>>

注:输出ASCII码数字对应的值,ord用法正好相反

4.7匿名函数:

calc = lambda n:print(n)calc(5)############################5

注:匿名函数的用法,而且匿名函数是只调用一次程序就回收这部分的内存空间,即立即释放

4.8匿名函数结合filter方法:

res = filter(lambda n:n>5,range(10))for i in res: print(i)############################################6789

注:fileter的作用是把合格的结果过滤出来

4.9匿名函数结合map方法:

res = map(lambda n:n*n,range(10))for i in res: print(i)####################################0149162536496481

注:map的作用是将每个数字都匹配条件然后输出

4.10reduce函数用法

import functoolsres = functools.reduce( lambda x,y:x+y,range(10))print(res)####################################################45

注:将每个循环的结果都加起来算出最终的结果

4.11globals函数

>>> print(globals()){'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None, '__builtins__': <module 'builtins' (built-in)>, '__package__': None, '__name__': '__main__', '__spec__': None}>>>

注:输出当前的全局变量

4.12divmod函数

>>> divmod(5,2)(2, 1)>>> divmod(10,2)(5, 0)

注:输出“商”取“余”

4.13只读集合函数

>>> a = set([1,1,2,3,4,3])>>> a.add(123213121)>>> a{1, 2, 3, 4, 123213121}>>> a = frozenset([1,2,3,1,2,3,2])

注:frozenset相当于元组(只读列表)的作用,不能修改等操作

4.14十六进制转换

>>> hex(255)'0xff'>>> hex(25)'0x19'

4.15八进制转换

>>> oct(10)'0o12'>>> oct(1)'0o1'>>> oct(2)'0o2'>>> oct(4)'0o4'>>> oct(5)'0o5'>>> oct(6)'0o6'>>> oct(7)'0o7'>>> oct(8)'0o10'

4.16n的n次方

>>> pow(2,8)256

4.17有序字典及排序

>>> a = {1:2,3:3,-5:1}>>> a{1: 2, 3: 3, -5: 1}>>> print(sorted(a.items())) [(-5, 1), (1, 2), (3, 3)]>>> print(sorted(a.items(),key=lambda x:x[1])) [(-5, 1), (1, 2), (3, 3)]

注:按照key培训以及按照value排序

4.18zip“拉链”函数:

a = [1,2,3,4,5,6]b = ['a','b','c','d','e','f','g']for i in zip(a,b): print(i)#########################################(1, 'a')(2, 'b')(3, 'c')(4, 'd')(5, 'e')(6, 'f')

注:如果有一方数据少了,那么合并数据时只会合并少的那方的个数

4.19引入自定义函数

__import__('xxx')

注:如果想引进自己写好的函数或者某个功能,但是只记得名字,就用这种方式导入


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表