本文讨论python中的三器一解析:迭代器,生成器,装饰器,列表解析。以及python中的深浅拷贝
一.迭代器
1.迭代器协议
(1)迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stopiteration异常,以终止迭代(只能往后走不能往前退)
(2)可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
(3)迭代器是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器访问对象。
2.for循环机制
for循环的本质:循环所有的对象,全都使用迭代器协议。
一个问题:for循环的本质就是遵循迭代器协议去访问对象,那么for循环的对象就肯定都是迭代器了啊,那既然这样,因为for循环可以遍历(字符串,列表,元组,字典,集合,文件对象),那么说明这些类型的诗句肯定都是可迭代对象。那么问题就来了:为什么定义一个列表l=[1,2,3,4],该列表却没有next()方法呢?
这里需要注意:(字符串,列表,元组,字典,集合,文件对象)这些都不是可迭代对象,他们之所以能用for循环遍历,是因为for循环调用了他们内部的__iter__方法,把他们变成了可迭代对象
然后for循环调用可迭代对象的__next__方法去取值,而且for循环会捕捉Stoplteration异常,以终止循环
1 l=[‘a‘,‘b‘,‘c‘] 2 3 #一:下标访问方式 4 print(l[0]) 5 print(l[1]) 6 print(l[2]) 7 #print(l[3]) #超出边界报错:IndexError 8 # 9 # #二:遵循迭代器协议访问方式 10 diedai_l=l.__iter__() #调用__iter__方法将列表转为可迭代对象 11 print(diedai_l.__next__()) 12 print(diedai_l.__next__()) 13 print(diedai_l.__next__()) 14 #print(diedai_l.__next__()) #超出边界,报错:StopIteration 15 16 #三:for循环访问方式 17 #for循环l的本质就是遵循迭代器协议的访问方式,xian调用diedai_l=l.__iter__() 18 #方法,或者直接diedai_l=iter(l),然后依次执行diedai_l.next(),直到for循环buhuo1 19 #到StopIteration终止循环 20 #for循环所有的对象的本质都是一样的原理 21 22 for i in l: #diedai_l=l.__iter__() 23 print(i) #i=diedai_l.next() 24 #四:用while去模拟for循环所做的事情 25 diedai_l=l.__iter__() 26 while True: 27 try: 28 print(diedai_l.__next__()) 29 except StopIteration: 30 print(‘迭代完毕了,循环终止了‘) 31 break
通过上面的例子可以看到,对于列表操作,索引操作是最为方便的,所以便产生一个问题:为什么要用for循环?没错,在列表这类数据类型来说,for循环确实没有什么优势,但是对于那些无序的数据类型呢?比如集合,字典,文件,这些数据类型只能用for循环遍历。因此for循环是为所有的数据类型提供一种统一的遍历方式。
最后补充一点,l.next()与l.__next__()是没有区别的,因为next()函数就是去调用的内置的__next()函数
二:生成器
1.生成器定义
生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他类型的数据需要调用自己内置的__iter__方法),所以生成器是可迭代对象
2.生成器分类:
(1)生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句异常返回一个结果,在每一个结果中间,挂起函数状态,以便下次从它离开的地方继续执行
1 def test(): 2 yield 1 3 yield 2 4 yield 3 5 f=test() #返回一个迭代器对象 6 print(f.__next__()) #返回迭代器中的第一个元素 7 print(f.__next__()) #返回迭代器中的第二个元素 8 print(f.__next__()) #返回迭代器中的第三个元素
(2)生成器表达式:类似于列表推导,但是,生成器按需产生结果的一个对象,而不是一次构建一个结果列表。
生成器表达式跟列表解析基本类似,只是使用列表解析是把表达式用[]括起来,而生成器表达式是用()括起来:
1 li=[‘鸡蛋%d‘ %i for i in range(10)] #列表解析 2 li1=(‘鸡蛋%d‘ %i for i in range(10)) #生成器表达式 3 print(li) #li是一个列表,直接输出即可 4 print(li1.__next__()) #li1是一个可迭代对象,需要使用next()遍历 5 print(li1.__next__()) 6 print(next(li1))
三:列表解析和三元表达式
1.三元表达式:
从一个简单的例子开始三元表达式的讨论。下面的代码实现了返回两个数字中的较大值:
1 a=3 2 b=4 3 def max_two(x,y): 4 if x>y: 5 return x 6 else: 7 return y 8 s=max_two(a,b) 9 print(s)
为了实现这个功能,我们定义了一个函数max_two,这个函数传入两个参数,然后通过比较两个数字的大小,返回较大的那一个值,因为这个函数比较简单,因此可以用一个三元表达式来直接实现:
1 a=3 2 b=4 3 s=a if a>b else b 4 print(s)
这段代码实现的功能与上面的代码一致,不同之处在于用第3行的三元表达式代替了max_two函数。所谓三元表达式,就是一个表达式有三部分。第三行的代码中,首先来看if a>b这一部分是三元表达式的中心部分,这一部分做了一个判断,如果if条件成立,则三元表达式的返回值是a,也就是三元表达式的第一部分是第二部为Ture时三元表达式的值,第三部分是else b,这一部分是在中心部分(第二部分)为False时三元表达式的返回值。
2.列表解析
有了三元表达式,就可以讨论列表解析了。先看一下例子,下面的代码实现了把10个元素插入到一个空列表中:
1 li=[] 2 for i in range(10): 3 li.append(‘鸡蛋%d‘ %i) 4 5 print(li)
上面的代码可以使用列表解析来改写:
1 li=[‘鸡蛋%d‘ %i for i in range(10)] 2 print(li)
这部分代码用一行代码就代替了上面的前3行代码。这就是使用列表解析的好处。现在来分析第一行的列表解析式:在列表解析式中,首先定义一个列表li=[],然后在[]中定义生成列表元素的表达式,该表达式分为两部分,第一部分是一个格式化表达式,这部分代表这个列表解析表达式对数据的操作,也就是生成数据的方式,后面部分指明了数据的范围。
四:深浅拷贝
1.浅拷贝
浅拷贝指的是在拷贝一个变量时,只会拷贝这个变量的第一层元素,浅拷贝的概念可以和元组做对比,前面讲过,元组的元素是不可以改变的,这个不可改变指的是元组的第一层元素不可以改变,也就是如果在元组的某个位置是一个列表,那么这个位置就只能是列表,不能是其他数据类型,但是列表中的元素是可以改变的。
有了对元组元素不可以改变的理解,现在再来看浅拷贝的概念。一般意义上的拷贝(copy),指的是会复制一份新的数据,这个数据与原来的那份数据没有任何关系,也就是下面代码所展示的:
1 li=[1,2,3,4,5] 2 l=li.copy() #浅拷贝,将li的数据拷贝一份给l 3 li[0]=‘hello‘ #因为li和l是两个变量,因此改变li的值不会影响l的元素 4 print(li) 5 print(l)
但是如果在一个序列中嵌套了另外一个序列呢?比如在列表l中嵌套了列表l1,此时复制一份l的数据给l2,然后我改变l2中的l1中的元素,那么l中l1的元素会改变吗?根据上面的例子来看,似乎是不会变的,因为这两个变量是两个不同的东西:
1 l=[1,2,3,[1,2,4,5]] #列表l中嵌套了列表l1 2 l2=l.copy() #将l中的数据拷贝一份给l2,浅拷贝 3 l2[3][0]=‘hello‘ #将l2中嵌套的l1的第二个元素改为hello 4 print(l) 5 print(l2)
根据我们前面的分析,这段代码只是改变了l2中的元素,不会改变l中的元素,现在来看看执行结果:
对比结果发现,l的元素也改变了。这与我们预想不一致,这是为什么呢?这就是深浅拷贝的区别了,浅拷贝和元组有点类似,他虽然是复制一份数据给另一个变量但是只是拷贝了第一层,即只是把嵌套列表的形式拷贝过去了,但是嵌套列表里面的内容是共享的,因此我们如果改变了一个列表的嵌套列表的元素,另一个列表也会发生改变。
2.深拷贝
要想拷贝成两个完全不同的变量,即修改一个列表里面嵌套列表的值,不会影响另外一个列表,就需要用到深拷贝了:
1 import copy #深拷贝需要导入copy 2 l=[1,2,3,[1,2,4,5]] 3 l2=copy.deepcopy(l) #将l中的数据拷贝一份给l2,深拷贝,两个列表成为了完全独立的列表 4 l2[3][0]=‘hello‘ #将l2中嵌套的l1的第二个元素改为hello,此时不会改变l中的元素 5 print(l) 6 print(l2)
深拷贝执行结果:
通过执行结果可以发现,深拷贝能达到我们想要的效果。
五:装饰器
1.装饰器概念
装饰器本质上是一个函数,这个函数是用来为其他函数添加功能的,装饰器有两个特性:第一,装饰器不会改变被修饰的函数的源代码,第二,装饰器不会改变被修饰函数的调用方式。一个函数只有同时满足这两个条件,才能被称为一个装饰器。
下面通过一个直观的例子来说明装饰器的作用。下面有一个简单的计算序列元素的和的函数:
1 import time 2 3 def cal(l): 4 time.sleep(1) 5 res=l.pop(0) 6 for i in l: 7 res+=i 8 return res 9 10 l=[i for i in range(10)] 11 res=cal(l) 12 print(res)
现在有一个新需求,需要计算这个函数的运行时间。当然,我们可以把这个功能直接加在函数内部,但是在实际开发中,往往是不允许对写好的函数再做内部逻辑修改,因为这样很容易影响到其他的功能,而且,最好是不需要改变函数的调用方式,因为这个函数可能在多处被调用,如果改变了调用方式,那么很多地方都要改,这样很容易出错。因此想要即可以为函数添加附加功能,又想要不修改函数的源代码,还要尽可能保持调用方式不变,那么最好的选择就是写一个装饰器了。因为装饰器既不会改变被修饰的函数的调用方式,也不会改变其源代码。下面先直观的感受一下使用装饰器实现该功能:
1 import time 2 def timmer(fun): #定义一个装饰器函数 3 def wrapper(*args,**kwargs): 4 start_time=time.time() 5 res=fun(*args,**kwargs) 6 end_time=time.time() 7 print(‘函数执行时间为%s‘ %(end_time-start_time)) 8 return res 9 return wrapper 10 11 @timmer #调用装饰器 12 def cal(l): #被装饰的函数 13 time.sleep(1) 14 res=l.pop(0) 15 for i in l: 16 res+=i 17 return res 18 19 20 l=[i for i in range(10)] 21 res=cal(l) 22 print(res)
通过上面代码,发现我们只是额外定义了一个装饰器函数,并没有改变cal的源代码,也没有改变其调用方式,因此很好的实现了我们的需求。下面就来讨论写一个装饰器的步骤:
2.装饰器的必要元素
一个装饰器由三部分组成:高阶函数,函数嵌套,闭包,即装饰器=高阶函数+函数嵌套+闭包。因此在写装饰器之前,需要先明白装饰器的这三个组成部分,下面来分别讨论装饰器的这三个部分
(1)高阶函数
前面函数部分已经简单说过高阶函数,高阶函数的定义如下:
a.函数接收的参数是一个函数名
b.函数的返回值是一个函数
满足这两个条件之一就可以称一个函数为高阶函数
1 def foo(fun): #函数的参数是一个函数 2 res=fun() 3 return res 4 5 def foo1(): 6 def tesr1(): 7 print(‘hahah‘) 8 return 0 9 return tesr1 #函数返回值是一个函数
上面定义的两个函数,foo传入的参数是一个函数,foo1返回值是一个参数,这两个函数都称为高阶函数。
下面我们尝试利用高阶函数来写一个装饰器:
1 import time 2 def foo(): 3 time.sleep(3) 4 print(‘这是一条来自被修饰的函数的语句‘) 5 6 #foo() 7 8 #装饰器的原则: 9 #1.装饰器不修改原函数的源代码 10 #2.装饰器不改变原函数的调用方式 11 12 def timmer(func): 13 start_time=time.time() 14 func() #在此处执行foo函数,实现了没有改变foo源代码功能 15 end_time=time.time() 16 print(‘函数的执行时间为:%s‘ %(end_time-start_time)) 17 return func 18 # res=timmer(foo) 19 # res() #调用foo,改变了foo的调用方式,为了不改变foo的调用方式,做一个小调整 20 foo=timmer(foo) 21 foo() #实现了不改变函数的调用方式
通过上面的d代码,我们似乎实现了装饰器的功能:既没有改变原函数的源代码,也没有改变原函数的调用函数。一切看上去似乎都很完美,但是看看下面的运行结果就会发现不那么美好了:
通过上面的运行结果发现,原函数运行了两次,而装饰器只是为原函数增加功能,不会让函数执行两次,因此上面的装饰器不合格!要想解决这个问题,就需要用到函数嵌套和闭包
(2)函数嵌套和闭包
函数嵌套,即在函数中定义函数,嵌套函数需要注意的是函数的作用域,即嵌套函数只是一个局部变量,他的作用仅限于他所定义的这一层。闭包的概念就是为了方便来区分嵌套函数的作用域的。
1 def farther(name): 2 print("father is %s" % name) 3 def son(): 4 name1=‘hah‘ 5 print(‘Son is %s‘ % name1) 6 def grandson(): 7 name2=‘hello‘ 8 print(‘gradnson is %s‘ % name2) 9 return 0 10 11 return grandson 12 13 return son 14 15 son=farther(‘nihao‘) 16 grandson=son() 17 grandson()
上面的代码,在farther函数中嵌套了一个函数son,又在函数son中嵌套了一个函数grandson.在函数中,需要考虑一个变量的作用范围,一个函数就算做一层,里面如果嵌套了函数,那么这个函数就算做另一层了,即一层中嵌套了另一层。有了层级关系,就会涉及到变量的作用范围,前面讲过一个变量被执行时,怎么去找变量的方法,即首先会在当前层级找这个变量的定义,如果当前层级没有该变量,再去上一层级找该变量,如果没找到,则再上一层,直至找到这个变量。
为了将不同层级的变量区分开,Python引入了闭包的概念,闭包中的包指的是把不同层级的函数分别封装成一个包裹,闭则指的是在这一层级所封装的局部变量有哪一些。就上面的例子来说,函数grandson是一个包裹,该包裹封装了变量name2,这是最里层的包裹,然后在外面套了一个包裹son函数,son函数这个包裹里面包装了grandson这个函数,另外,他还包装了变量name1.而在son函数外面还有一个包裹farther函数,这个函数包裹了函数son,另外还有一个变量name。函数就是通过闭包的概念来区分不同的变量的作用域,每一个局部变量的作用域仅限于本包裹,如果某个变量在本包裹找不到,那么就去上一个包裹去找。
有了函数嵌套和闭包,就可以开始修改上面的装饰器了:
1 import time 2 def foo(): 3 time.sleep(3) 4 print(‘这是一条来自被修饰的函数的语句‘) 5 6 #foo() 7 8 #装饰器的原则: 9 #1.装饰器不修改原函数的源代码 10 #2.装饰器不改变原函数的调用方式 11 12 def timmer(func): 13 def wrapper(): 14 start_time=time.time() 15 func() 16 end_time=time.time() 17 print(‘函数的执行时间为:%s‘ %(end_time-start_time)) 18 return wrapper 19 # res=timmer(foo) 20 # res() #调用foo,改变了foo的调用方式,为了不改变foo的调用方式,做一个小调整 21 foo=timmer(foo) 22 foo()
这段代码,在装饰器部分嵌套了一个函数,这样就完美解决了让函数执行两次的问题。分析一下上面的代码,在装饰器timmer中定义了一个wrapper函数,然后把求foo函数运行时间的代码写在了wrapper函数里面,timmer函数返回的是wrapper函数,这样,第21行执行时,timmmer函数返回的就是wrapper函数的地址,然后第22行执行foo()函数实际上是在执行wrapper函数,这一点非常重要。即调用被修饰的函数实际上是在调用装饰器内部的嵌套函数。
上面的代码已经基本上实现了装饰器,但是还有一个小问题就是我们多出来了第21行这一句,如果每一个被修饰的函数都要加上这一句,无疑是很麻烦的,而且很容易出错。python为了处理这个问题,引入了@timmmer的概念,即在被修饰的函数的开始加上@装饰器函数名,python就会自动完成第21行所做的工作:
1 import time 2 3 def timmer(func): 4 def wrapper(): 5 start_time=time.time() 6 func() 7 end_time=time.time() 8 print(‘函数的执行时间为:%s‘ %(end_time-start_time)) 9 return wrapper 10 11 12 @timmer #该语句等价于 foo=timmer(foo) 13 def foo(): 14 time.sleep(3) 15 print(‘这是一条来自被修饰的函数的语句‘)
至此,一个完整的装饰器就算写完了。但是因为我们原函数比较简单,因此装饰器也就很容易实现,现在我们对原函数做一下拓展。
3.原函数有返回值的装饰器
上面实现的装饰器,其原函数是没有返回值的,那么对于有返回值的原函数,其装饰器该怎么写呢?比如要给下面的原函数加上一个计算运行时间的装饰器:
1 import time 2 def foo(): 3 time.sleep(1) 4 print(‘这是一条来自原函数的语句‘) 5 li1=[i for i in range(100)] 6 return li1 #原函数返回一个列表
针对这样的原函数,因为调用原函数等价于调用装饰器的嵌套函数,因此只需要在嵌套函数内部想办法返回原函数的返回值就好了:
1 import time 2 3 def timmer(func): 4 def wrapper(): 5 start_time=time.time() 6 res=func() #调用func函数,用res变量接收func函数的返回值 7 end_time=time.time() 8 print(‘%s 函数的执行时间是%s‘ %(func,(end_time-start_time))) 9 return res #wrapper函数返回res,因为调用foo函数等价于调用wrapper函数 10 return wrapper 11 12 @timmer #等价于foo=timmer(foo) 13 def foo(): 14 time.sleep(1) 15 print(‘这是一条来自原函数的语句‘) 16 li1=[i for i in range(100)] 17 return li1 #原函数返回一个列表 18 19 res=foo() 20 print(res)
从上面的代码可以看出,因为第19行调用foo的语句等价于调用wrapp函数,因此为了能在外部接收到foo的返回值,需要在装饰器调用原函数的地方设置一个变量接收原函数的返回值,然后用装饰器的嵌套函数返回该变量即可,这样就可以在外部接收该变量了。
4.原函数有参数的装饰器
上面讨论了有返回值的装饰器,现在更进一步,如果原函数有参数该怎么为函数加上装饰器,比如下面的函数:
1 import time 2 3 def test(name,age): 4 time.sleep(1) 5 print(‘my name is %s,age is %s‘ %(name,age)) 6 return 9
这个函数有两个参数,现在考虑怎么把这两个参数用装饰器表现出现,因为有了装饰器,调用原函数实际上是在调用装饰器的嵌套函数,所以一个很直观的想法是给嵌套函数传入两个参数:
1 import time 2 def timmer(func): 3 def wrapper(name,age): #因为调用test函数实际上是在调用该函数,因此在此处传入两个参数 4 start_time=time.time() 5 res=func(name,age) #调用test函数,将wrapper函数的参数原封不动的传给func 6 end_time=time.time() 7 print(‘函数的执行时间是%s‘ %(end_time-start_time)) 8 return res 9 return wrapper 10 11 @timmer #等价于test=timmer(test) 12 def test(name,age): 13 time.sleep(1) 14 print(‘my name is %s,age is %s‘ %(name,age)) 15 return 9 16 17 res=test(‘nihao‘,23) 18 print(res)
上面的代码确实很好的实现了原函数有参数的情形,但是请考虑一下装饰器的作用,装饰器是为了来修饰函数的,因此它应该不依赖于某一个特定的函数,而上面的装饰器只能用来修饰有两个参数的函数类型,如果现在需要用这个装饰器去修饰有三个参数的原函数,那么这个装饰器就是一点办法都没有了,究其关键,就是因为原函数的参数个数限制了装饰器的使用范围。那么有没有一种办法可以让装饰器不受原函数形参个数的影响呢?答案是有的,这便是前面函数里面说的*args和**kwargs参数了,这两个参数形式是可以传入人一个参数的,其他*args代表位置参数,**kwargs代表关键字参数(字典参数)
1 import time 2 def timmer(func): 3 def wrapper(*args,**kwargs): #*args表示原函数可以有任意个位置参数,**kwargs代表原函数可以有任意个关键字参数 4 start_time=time.time() 5 res=func(*args,**kwargs) #调用test函数,将wrapper函数的参数原封不动的传给func 6 end_time=time.time() 7 print(‘函数的执行时间是%s‘ %(end_time-start_time)) 8 return res 9 return wrapper 10 11 @timmer #等价于test=timmer(test) 12 def test(name,age): 13 time.sleep(1) 14 print(‘my name is %s,age is %s‘ %(name,age)) 15 return 9 16 17 res=test(‘nihao‘,23) 18 print(res)
至此,装饰器的内容就算全部完成了,无参,有参,有返回值,无返回值的情形已经全部涉及到了。最后通过一个例子补充一下如果需要给装饰器传参数该怎么处理(前面的内容已经可以处理百分之80左右的问题了)
4.一个例子
考虑京东首页,我们用浏览器打开京东,第一个页面就是显示京东首页,然后京东首页有很多模块,比如购物车,家目录,京东主页,如果需要操作购物车和家目录,则必须要登录账号才行,现在的需要就是做一个登录的验证,即当用户点击这些功能区时,会验证当前有没有用户登录,如果没有,那么就需要跳转到登录页面,此处我们只做登录验证,即提醒用户输入账号和密码,不做页面跳转:
1 import time 2 3 def auth_type(func): 4 def wrapper(*args,**kwargs): 5 username=input(‘请输入用户名‘).strip() 6 passwd=input(‘请输入密码‘).strip() 7 if username==‘sb‘ and passwd==‘1234‘: 8 res=func(*args,**kwargs) 9 return res 10 else: 11 print(‘用户名或密码错误,请重新输入‘) 12 return wrapper 13 14 @auth_type 15 def home(): 16 time.sleep(1) 17 print(‘欢迎回家‘) 18 return 1 19 20 @auth_type 21 def gouwuche(name): 22 time.sleep(1) 23 print(‘%s 的购物车有%s‘ %(name,[‘book‘,‘bi‘,‘hah‘])) 24 return 2 25 26 @auth_type 27 def index(name,age=100): 28 time.sleep(1) 29 print(‘欢迎来到京东‘) 30 print(‘%s,%s‘ %(name,age)) 31 return 3 32 33 home() 34 gouwuche(‘hahah‘) 35 index(‘heher‘)
上面的代码实现了登录验证的功能,但是有个缺陷是每一个点击其他功能,都需要重新输入账号密码,比如,从购物车切回主页的时候就需要从新登录一次。在实际中肯定不能这样,只需要登录一次就可以了,因此这里需要做一个小小的修改,需要一个变量来存放登录状态:
1 import time 2 logion_state={‘username‘:None,‘logion‘:False} #存放登录状态 3 def auth_type(func): 4 def wrapper(*args,**kwargs): 5 if logion_state[‘username‘] and logion_state[‘logion‘]: #若条件为真,则说明已经登录过了,不需要再登录 6 res=func(*args,**kwargs) 7 return res 8 else: 9 username=input(‘请输入用户名‘).strip() 10 passwd=input(‘请输入密码‘).strip() 11 if username==‘sb‘ and passwd==‘1234‘: 12 res=func(*args,**kwargs) 13 logion_state[‘username‘]=username 14 logion_state[‘logion‘]=True 15 #res=func(*args,*kwargs) 16 return res 17 else: 18 print(‘用户名或密码错误,请重新输入‘) 19 return wrapper 20 21 @auth_type 22 def home(): 23 time.sleep(1) 24 print(‘欢迎回家‘) 25 return 1 26 27 @auth_type 28 def gouwuche(name): 29 time.sleep(1) 30 print(‘%s 的购物车有%s‘ %(name,[‘book‘,‘bi‘,‘hah‘])) 31 return 2 32 33 @auth_type 34 def index(name,age=100): 35 time.sleep(1) 36 print(‘欢迎来到京东‘) 37 print(‘%s,%s‘ %(name,age)) 38 return 3 39 40 home() 41 gouwuche(‘hahah‘) 42 index(‘heher‘)
上面的代码已经实现了只登陆一次的功能,现在更进一步,上面的代码,在验证用户名和密码时,我们是把用户名和密码写死了的,但是在实际开发中,应该是去取数据库中的数据验证,因此需要把上面的验证改的更灵活一些,让他可以去取数据库中的数据验证:
1 import time 2 3 name_dic=[ 4 {‘name‘:‘a‘,‘passwd‘:‘1234‘}, 5 {‘name‘:‘b‘,‘passwd‘:‘abcd‘}, 6 {‘name‘:‘c‘,‘passwd‘:‘wxyz‘}, 7 {‘name‘:‘d‘,‘passwd‘:‘defe‘} 8 9 ] 10 logion_state={‘username‘:None,‘logion‘:False} #存放登录状态 11 def auth_type(func): 12 def wrapper(*args,**kwargs): 13 if logion_state[‘username‘] and logion_state[‘logion‘]: #若条件为真,则说明已经登录过了,不需要再登录 14 res=func(*args,**kwargs) 15 return res 16 else: 17 username=input(‘请输入用户名‘).strip() 18 passwd=input(‘请输入密码‘).strip() 19 for item in name_dic: 20 if username==item[‘name‘] and passwd==item[‘passwd‘]: 21 res=func(*args,**kwargs) 22 logion_state[‘username‘]=username 23 logion_state[‘logion‘]=True 24 #res=func(*args,*kwargs) 25 return res #此处使用return语句结束for循环,不必使用break 26 else: 27 print(‘用户名或密码错误,请重新输入‘) 28 return wrapper 29 30 @auth_type 31 def home(): 32 time.sleep(1) 33 print(‘欢迎回家‘) 34 return 1 35 36 @auth_type 37 def gouwuche(name): 38 time.sleep(1) 39 print(‘%s 的购物车有%s‘ %(name,[‘book‘,‘bi‘,‘hah‘])) 40 return 2 41 42 @auth_type 43 def index(name,age=100): 44 time.sleep(1) 45 print(‘欢迎来到京东‘) 46 print(‘%s,%s‘ %(name,age)) 47 return 3 48 49 home() 50 gouwuche(‘hahah‘) 51 index(‘heher‘)
现在来完成终极操作:上面的代码虽然已经实现了从数据库中去验证用户名和密码,但是还有一个问题就是,上面的代码只能验证某一种特定的数据结构,而在实际开发中,可能不同的功能用到的是不同的数据结构,为了能够处理这种情况,即验证用户名时可以验证不同的数据结构中的数据,就需要为装饰器函数加一个参数,为了能给装饰器加一个参数,需要在装饰器外面再套一层函数:
1 import time 2 3 name_dic=[ 4 {‘name‘:‘a‘,‘passwd‘:‘1234‘}, 5 {‘name‘:‘b‘,‘passwd‘:‘abcd‘}, 6 {‘name‘:‘c‘,‘passwd‘:‘wxyz‘}, 7 {‘name‘:‘d‘,‘passwd‘:‘defe‘} 8 9 ] 10 logion_state={‘username‘:None,‘logion‘:False} #存放登录状态 11 def auth(a_type=‘filedb‘): 12 def auth_type(func): 13 def wrapper(*args,**kwargs): 14 print(‘验证类型是%s‘,a_type) 15 if a_type==‘filab‘: 16 if logion_state[‘username‘] and logion_state[‘logion‘]: #若条件为真,则说明已经登录过了,不需要再登录 17 res=func(*args,**kwargs) 18 return res 19 else: 20 username=input(‘请输入用户名‘).strip() 21 passwd=input(‘请输入密码‘).strip() 22 for item in name_dic: 23 if username==item[‘name‘] and passwd==item[‘passwd‘]: 24 res=func(*args,**kwargs) 25 logion_state[‘username‘]=username 26 logion_state[‘logion‘]=True 27 #res=func(*args,*kwargs) 28 return res #此处使用return语句结束for循环,不必使用break 29 else: 30 print(‘用户名或密码错误,请重新输入‘) 31 elif a_type==‘tix‘: 32 print(‘我也不知道是什么鬼‘) 33 res=func(*args,**kwargs) 34 return res 35 else: 36 print(‘你这是啥?‘) 37 res=func(*args,**kwargs) 38 return res 39 return wrapper 40 return auth_type 41 42 @auth(a_type=‘filedb‘) 43 def home(): 44 time.sleep(1) 45 print(‘欢迎回家‘) 46 return 1 47 48 @auth(a_type=‘txt‘) 49 def gouwuche(name): 50 time.sleep(1) 51 print(‘%s 的购物车有%s‘ %(name,[‘book‘,‘bi‘,‘hah‘])) 52 return 2 53 54 @auth(a_type=‘hdie‘) 55 def index(name,age=100): 56 time.sleep(1) 57 print(‘欢迎来到京东‘) 58 print(‘%s,%s‘ %(name,age)) 59 return 3 60 61 home() 62 gouwuche(‘hahah‘)
至此,装饰器的所有内容都已完成。