Python - 三大神器 迭代器,生成器,装饰器

在介绍三大器之前先来了解一下容器和可迭代对象...

一.容器

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特例,并不是所有的元素都放在内存,比如迭代器和生成器对象)在Python中,常见的容器对象有:

  • list, deque...
  • set, frozensets(不可变集合)...
  • dict, defaultdict, OrderedDict, Counter...
  • tuple, namedtuple...
  • str

容器的概念就像一个盒子,可以往里面装东西.当它可以用来询问某个元素是否包含在其中时,那么这个对象就可以认为是一个容器,比如 list,set,tuples都是容器对象:

>>> assert 1 in [1, 2, 3]     # lists

>>> assert 4 not in [1, 2, 3]

>>> assert 1 in {1, 2, 3}     # sets

>>> assert 4 not in {1, 2, 3}

>>> assert 1 in (1, 2, 3)     # tuples

>>> assert 4 not in (1, 2, 3)

询问某元素是否在dict中用dict的中key:

>>> d = {1: 'foo', 2: 'bar', 3: 'qux'}

>>> assert 1 in d

>>> assert 'foo' not in d     # 'foo' 不是dict中的元素

询问某substring是否在string中:

>>> s = 'foobar'

>>> assert 'b' in s

>>> assert 'x' not in s

>>> assert 'foo' in s

尽管绝大多数容器都提供了某种方式来获取其中的每一个元素,但这并不是容器本身提供的能力,

而是可迭代对象赋予了容器这种能力,当然并不是所有的容器都是可迭代的,比如:Bloom filter,

虽然Bloom filter可以用来检测某个元素是否包含在容器中,但是并不能从容器中获取其中的每一个值,

因为Bloom filter压根就没把元素存储在容器中,而是通过一个散列函数映射成一个值保存在数组中。

 

二.可迭代对象(iterable)

大部分对象都是可迭代,只要实现了__iter__方法的对象就是可迭代的。
__iter__方法会返回迭代器(iterator)本身,例如:

>>> lst = [1,2,3]

>>> lst.__iter__()

<listiterator object at 0x7f97c549aa50>

Python提供一些语句和关键字用于访问可迭代对象的元素,比如for循环、列表解析、逻辑操作符等。

 

判断一个对象是否是可迭代对象:

>>> from collections import Iterable     # 只导入Iterable方法

>>> isinstance('abc', Iterable)

True

>>> isinstance(1, Iterable)

False

>>> isinstance([], Iterable)

True

这里的isinstance()函数用于判断对象类型。

可迭代对象一般都用for循环遍历元素,也就是能用for循环的对象都可称为可迭代对象。

例如,遍历列表:

>>> lst = [1, 2, 3]

>>> for i in lst:

... print i

...

三.迭代器

迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stoplteration异常,以终止迭代(只能往后走不能往前退)

实现了迭代器协议的对象(对象内部定义了一个__iter__()方法)

python中的内部工具(如for循环,sum,min,max函数等)基于迭代器协议访问对象。

迭代器的特点:

  

  1. 省内存 -> 生成器

  2. 惰性机制

  3. 只能向前. 不能反复

 

判断是否是迭代器:

>>> from collections import Iterator

>>> isinstance(d, Iterator)

False

>>> isinstance(d.iteritems(), Iterator) True

使用next方法:

>>> iter_items = d.iteritems()

>>> iter_items.next()

('a', 1)

>>> iter_items.next()

('c', 3)

>>> iter_items.next()

('b', 2)

迭代器的原理:

1 #基于迭代器协议

2 li = [1,2,3]

3 diedai_l = li.__iter__()

4 print(diedai_l.__next__())

5 print(diedai_l.__next__())

6 print(diedai_l.__next__())

7 # print(diedai_l.__next__()) # 超出边界报错

8

9 #下标

10 print(li[0])

11 print(li[1])

12 print(li[2])

13 # print(li[3]) # 超出边境报错

14

15 # 用while循环模拟for循环机制

16 diedai_l = li.__iter__()

17 while True:

18 try:

19 print(diedai_l.__next__())

20 except StopIteration:

21 print("迭代完毕,循环终止")

22 break

23

24 # for循环访问方式

25 # for循环本质就是遵循迭代器协议的访问方式,先调用diedai_l=li.__iter__方法

26 # 或者直接diedai_l=iter(l),然后依次执行diedai_l.__next__(),直到捕捉到

27 # StopItearation终止循环

28 # for循环所有的对象的本质都是一样的原理

四.生成器

可以理解为一种数据类型,自动实现迭代器协议

   在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,

返回yield的值。并在下一次执行next()方法时从当前位置继续运行

1.表现形式:

  生成器函数 带yield的函数(1、返回值 2、保留函数的运行状态)

    next(t)  t.__next__  t.send(可以给上一层的yield传值)

# 用生成器函数

# yield 相当于return控制的是函数的返回值

# x=yield的另外一个特性,接收send传过来的值,赋值给x

def test():

"开始啦")

yield # return 1 first = None

"第一次",first)

yield 2

"第二次")

t = test()

print(test().__next__())

res = t.__next__() # next(t)

print(res)

res = t.send("函数停留在first那个位置,我就是给first赋值的")

print(res)

 

输出结果

开始啦

None

开始啦

None

第一次 函数停留在first那个位置,我就是给first赋值的

2.生成器表达式

print(sum(i for i in range(10000))) # 表达式一般用for循环 (i for i in range(10000))

# 作用 节省内存,在内部已经实现了__iter__的方法

 

 

五.装饰器

1.装饰器的作用

  • 装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功 能, 
  • 装饰器的返回值也是一个函数/类对象。 
  • 它有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用

2.不带参数的装饰器

def wrapper(func):

    def inner(*args,**kwargs):

        print('--函数前添加功能--')

        ret = func(*args,**kwargs)

       print('--函数后添加功能--')

       return ret

   return inner
#定义一个函数,并添加装饰器

@wrapper       #等价于 func1 = timer(func1)

def func1(m):

   print('--func1--')

   return m

#调用函数

f = func1(20)

print(f)

3.带参数的装饰器

def outer(flag):

   def timer(func):

       def inner(*args,**kwargs):

           if flag:

               print('--添加功能--')

               ret = func(*args,**kwargs)

           else:

               ret = func(*args,**kwargs)

           return ret

       return inner

   return timer

#定义函数并添加装饰器

@outer(True)  # func1 = timer()

def func1(m):

   print('--func1--')

   return m

#调用函数

print(func1(20))

4.多个装饰器装饰一个函数

def wrapper1(func):

   print('--wrapper1装饰--')

   def inner1(*args,**kwargs):

       print('--wrapper1 前--')

       ret = func(*args,**kwargs)

       print('--wrapper1 后--')

       return ret

   return inner1

def wrapper2(func):

   print('--wrapper2装饰--')

   def inner2(*args,**kwargs):

       print('--wrapper2 前--')

       ret = func(*args,**kwargs)

       print('--wrapper2 后--')

       return ret

   return inner2
#定义函数并添加装饰器

@wrapper2   #func1 = wrapper2(func1) inner2 func = inner1

@wrapper1   #func1 = wrapper1(func1) inner1 func = func1

def func1(m):

   print ('--func1--')

   return m

#调用函数

print(func1(20))

#执行结果:

# --开始装饰wrapper1-

# --开始装饰wrapper2-

# --wrapper2 前-# --wrapper1 前-

# --func1-# --wrapper1 后-

# --wrapper2 后-

# 20

5.装饰器修复技术

import time

from functools import wraps

def timer(func):

    @wraps(func) 

  def inner():

        print(time.time()) 

      ret = func() 

      return ret

    return inne

六.闭包

根据这句话,其实我们自己就可以总结出在python语言中形成闭包的三个条件,缺一不可:

1)必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套

2)内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量

3)外部函数必须返回内嵌函数——必须返回那个内部函数

python闭包的优点:

  • 避免使用全局变量
  • 可以提供部分数据的隐藏
  • 可以提供更优雅的面向对象实现

 

def funx():

x=5

def funy():

nonlocal x

1

return x

return funy