迭代器,生成器,列表解析,装饰器,深浅拷贝

Posted jiachuantang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了迭代器,生成器,列表解析,装饰器,深浅拷贝相关的知识,希望对你有一定的参考价值。

本文讨论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)

至此,装饰器的所有内容都已完成。

以上是关于迭代器,生成器,列表解析,装饰器,深浅拷贝的主要内容,如果未能解决你的问题,请参考以下文章

生成器和迭代器,深浅拷贝

笔试面试题实现

装饰器 迭代器 生成器 面相过程 三元表达式 列表解析 序列化

Python高级用法总结--(列表推导式,迭代器,生成器,装饰器)

python_day04 函数嵌套 名称空间和作用域 闭包 装饰器 迭代器 生成器 列表解析 三元表达式 生成器表达式

函数嵌套 ,名称空间与作用域 ,闭包函数 ,装饰器 ,迭代器, 生成器 三元表达式,列表解析,生成器表达式 递归与二分法, 内置函数