1
 

 

Python学习之三大名器-装饰器、迭代器、生成器

 

一、装饰器

    装饰,顾名思义就是在原来的基础上进行美化及完善,器这里指函数,所以说装饰器就是装饰函数,也就是在不改变原来函数的代码及调用方式的前提下对原函数进行功能上的完善。其核心原理其实是利用闭包。

    格式 @关键字+装饰函数

         被装饰函数() 

    注意:@行必须顶头写而且是在被装饰函数的正上方

    按照形式可以分为:无参装饰器和有参装饰器,有参装饰器即给装饰器加上参数

    以下示例是一个无参装饰器,为原函数添加了统计运行时间的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import  time
#定义装饰器 
def  timer(func):
     def  wrapper( * args, * * kwargs):
         start_time  =  time.time()
         res  =  func( * args, * * kwargs)
         stop_time  =  time.time()
         print ( "run time is %s"  % (stop_time - start_time))
         return  res
     return  wrapper
  
 
#调用装饰器
@timer
def  index():
     =  []
     for  in  range ( 10000000 ):
         l.append(i)
#调用阶段 
index()

   以下是一个有参装饰器,实现简单的认证功能,#数字表示程序依次执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def  auth2(auth_type):  #1 #3
     def  auth(func):  #4 #6
         def  wrapper( * args, * * kwargs):  #7 #10
             if  auth_type  = =  'file' #11
                 name = input ( 'username: ' )
                 password = input ( 'password: ' )
                 if  name  = =  'zhejiangF4'  and  password  = =  '666' :
                     print ( 'auth successfull' )
                     res = func( * args, * * kwargs)
                     return  res
                 else :
                     print ( 'auth error' )
             elif  auth_type  = =  'sql' #12
                 print ( 'nothing!' #13
         return  wrapper  #8
     return  auth  #5
  
@auth2 (auth_type = 'sql' #2
def  index():
     print ( 'welcome to inex page' )
 
  index()  #9

 

二、迭代器

    迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。------百度百科

 

    可迭代的:只要对象本身有__iter__方法,那它就是可迭代的

    执行对象下的__iter__方法,得到的结果就是迭代器

       

为什么要用迭代器:
    优点
    1:迭代器提供了一种不依赖于索引的取值方式,这样就可以遍历那些没有索 引的可迭代对象了(字典,集合,文件)
    2:迭代器与列表比较,迭代器是惰性计算的,更节省内存

        缺点:
    1:无法获取迭代器的长度,使用不如列表索引取值灵活
    2
一次性的,只能往后取值,不能倒着取值

查看s对象是否是迭代器:print(isinstance(s,Iterator)) 返回True就是迭代器

1
2
3
4
5
6
7
from  collections  import  Iterable,Iterator
s = 'hello'
l = [ 1 , 2 , 3 ]
t = ( 1 , 2 , 3 )
d = { 'a' : 1 }
set1 = { 1 , 2 , 3 , 4 }
f = open ( 'a.txt' )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
s.__iter__()
l.__iter__() 
t.__iter__()
d.__iter__()
set1.__iter__()
f.__iter__()
print ( isinstance (s,Iterable))
print ( isinstance (l,Iterable))
print ( isinstance (t,Iterable))
print ( isinstance (d,Iterable))
print ( isinstance (set1,Iterable))
print ( isinstance (f,Iterable))
 
print ( isinstance (s,Iterator))
print ( isinstance (l,Iterator))
print ( isinstance (t,Iterator))
print ( isinstance (d,Iterator))
print ( isinstance (set1,Iterator))
print ( isinstance (f,Iterator))

运行结果如下:

wKiom1juEpmyloHFAAAfNIJNLNA267.png

可以看出,字符串、列表、字典、集合、元组、文件都是可迭代的,但是只有文件是迭代器

 

三、生成器

    

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

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

    函数中包含yield语句的我们称其为生成器函数

    yield与return有何区别?
        return只能返回一次函数就彻底结束了,而yield能返回多次值
    yield到底干了什么事情:
        yield把函数变成生成器(生成器就是迭代器)
        函数在暂停以及继续下一次运行时的状态是由yield保存

    下例是两个生成器的应用,一个用来不断的输入url,不断的解析,另外一个则模仿Linux中的管道命令(实质是将一个函数的运行结果传给下一个函数做处理,实现的比较简单粗暴,多包涵,哈哈)

例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from  urllib.request  import  urlopen
def  get(url):
     while  True :
         def  index():
             return  urlopen(url).read()
         url  =  yield  index()
=  get( 'http://www.baidu.com' )
next (g)
def  run():
     while  True :
         url  =  input ( "请输入URL:" )
         if  'http://'  not  in  url:
             print (g.send( 'http://' + url))
         else :
             print (g.send(url))
run()

例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def  cat(filename):
     with  open (filename, 'r' ) as f:
         while  True :
             line  =  f.readline()
             if  not  line:
                 break
             else :
                 yield  line
def  grep(string,lines):
     for  line  in  lines:
         if  string  in  line:
             yield  line
g1  =  cat( 'a.txt' )
g2  =  grep( 'mac' ,g1)
if  __name__  = =  '__main__' :
     =  input ( "请输入命令:" ).strip()
     if  = =  "cat a.txt |grep mac" :
         for  in  g2:
             print (i)

补充:协程

    如果在一个函数内部yield的使用方式是表达式形式的话,如x=yield,那么该函数成为协程函数

直接看例子吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def  hello(func):
     def  wrapper( * args, * * kwargs):
         res  =  func( * args, * * kwargs)
         next (res)
         return  res
     return  wrapper
 
@hello
def  eater(name):
     print ( '%s start to eat food'  % name)
     food_list = []
     while  True :
         food = yield  food_list
         print ( '%s get %s ,to start eat'  % (name,food))
         food_list.append(food)
     print ( 'done' )
e = eater( "somebody" )
print (e.send( '巧克力' ))
print (e.send( "香蕉" ))