首页 > 技术文章 > 2019-07-15 迭代器、生成器、一部分内置函数

wangcuican 2019-07-15 22:29 原文

一、迭代器

  什么是迭代:更新换代的过程,每次的迭代都必须基于上一次的结果

  迭代器:迭代取值的工具

  为什么要用迭代器:迭代器给你提供了一种不依赖于索引取值的方式

  1.可迭代对象:内置有__iter__方法的对象,是可迭代对象object.__iter__

    基本数据类型中:是可迭代对象的有:

      str,list,set,dict,tuple

      'hello'.__iter__

      (1,2,3).__iter__       等等  

 

  2.迭代器对象

    可迭代对象执行object.__iter__()得到的结果就是迭代器对象

    迭代器对象既有__iter__方法又有__next__方法

    文件对象(执行内置的__iter__之后还是本身  没有任何变化):文件本身就是迭代器对象

    迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象

    迭代器取值  调用__next__ 

l = [1,2,3,4]
l1 = l.__iter__()   #变成迭代器对象了

print(l1.__next__())   #1
print(l1.__next__())  #2
print(l1.__next__())  #3
print(l1.__next__())  #4
print(l1.__next__())  #取值完了,就会报错

#__next__逐一取值

    迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身

    迭代器取值的特点:只能往后依次取,不能后退

    迭代取值:

      优点:

        1.不依赖于索引取值      2.内存中永远只占一份空间,不会导致内存溢出

      缺点:

        1.不能获取指定的元素   2.取完之后会报StopIteration错误

二、for 循环的本质(重点

  for循环后面的in关键字后面跟的是一个可迭代对象

  for循环内部的本质:(很重要)

    1.将in后面的可迭代对象调用__iter__变成迭代器对象  

    2.调用__next__迭代取值

    3.内部有异常捕获StopIteration,当__next__报这个错时,自动结束循环 

三、生成器

  生成器:就是用户自定义的迭代器,本质是迭代器。

  函数内部加了关键字yield就变成生成器,有yield关键字,那么加括号执行函数的时候不会触发函数体代码运行。yield后面的值需要调用__next__方法你才能获取到,运行到yield时候,运行会暂停。还需要下一个yield后面的值,你就还需要写一个__next__去获取。

  yield既可以返回一个值也可以返回多个值,多个值也是按照元组的形式返回

def index():
print('index')
yield 666
yield 777,888

g = index() #生成器初始化:将函数变成迭代器
print(g.__next__())
print(g.__next__())

#结果
#index
#666
#(777,888)

四、yield表达式形式(支持外界为其传参)   send方法

  当函数内有yield关键字的时候,调用该函数不会执行函数体代码,而是将函数变成生成器。

  用__next__是代码运行到yield暂停下来,返回yield的值,在这里yield没有值,等着我们传入。g.send(),传入的值给左边的变量,并且触发了__next__方法,然后又运行到yield暂停下来。

def dog(name):
    print('%s 准备开吃'%name)
    while True:
        food = yield
        print('%s 吃了 %s'%(name,food))

g= dog('wee')
g.__next__()   ## 必须先将代码运行至yield 才能够为其传值
g.send('狗不理包子')  # 给yield左边的变量传参  触发了__next__方法
g.send('饺子')

#结果
wee 准备开吃
wee 吃了 狗不理包子
wee 吃了 饺子

  总结:

    yield:

      1.帮你提供了一种自定义生成器方式

      2.会帮你将函数的运行暂停下来

      3.会有返回值

    与return的异同点:

      相同点:都有返回值,并且都可以返回多个

      不同点:yield可以返回多次值,return只能返回一次值就结束函数。yield还可以接受外部传入的值

五、生成器表达式

  生成器代码不会主动执行任何一行代码    必须通过__next__触发代码的运行   

  注意:for 在()里面就是一个生成器表达式,不会触发__next__方法(重点!!!!)

res = (i for i in range(1,100000000) if i != 4)
print(res)   #是一个生成器
print(res.__next__())  #1
print(res.__next__())  #2

 

面试题:生成器的运用

def add(n,i):
  return n+i

def
test(): for i in range(4): yield i g=test() for n in [1,10]: g=(add(n,i) for i in g) # 第一次for循环g=(add(n,i) for i in test()) #第一次循环不会运行,for在()括号里面,这是一个生成器表达式,n = 1 # 第二次for循环g=(add(n,i) for i in (add(n,i) for i in test())) #这里也是一个生成器表达式,n = 10 print(n) #所以后面的n都是等于10 res=list(g) ---------------------------------------------------------------
解析:
n = 10
解析:第二次for循环
第一步:放在list中就有__next__方法,可以变成
g = for i in (add(n,i) for i in test())
    add(n,i)
第二步:
然后里面还可以拆开: for i in test() >>> (0,1,2,3)
              add(n,i) >>>(10,11,12,13)
第三步: 
g = for i in (10,11,12,13)
    add(n,i) >>> (20,21,22,23)
res = list(g) >>> [20,21,22,23]
      
#最后结果返回的答案是什么
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]  答案
#D. res=[21,22,23,24]

六、内置函数

  1. abs()   #求绝对值

print(abs(-1.11))    #1.11

  2. all()/any()

l = [0,1,2]
#因为0是属于False
print(all(l))  # 只要有一个为False就返回False
print(any(l))  # 只要有一个位True就返回True

#False
#True

  3.chr()/ord()           应用: 在创建验证码的时候可以用

print(chr(97))  # 将数字转换成ascii码表对应的字符
print(ord('a'))  # 将字符按照ascii表转成对应的数字

#a
#97

  4.dir()   可以调用当前对象的所有内置方法和魔法方法

# dir获取当前对象名称空间里面的名字
l = [1,2,3]
print(dir(l))

  5.divmod()   应用:可以做分页器

  返回值(商,余数)

print(divmod(101,10))   #(10, 1)

  6.enumerate()  枚举

# enumerate 枚举
l = ['a','b']
for i,j in enumerate(l,1):  #设置起始值为1,默认为0
    print(i,j) 

# 1 a
# 2 b

  7.eval()/exec()    把字符串的外面引号去掉,然后运行代码返回结果。注意eval()不支持逻辑代码,只支持简单python代码

s = """
print('hello baby~')
x = 1
y = 2
print(x + y)

"""
# eval(s)
exec(s)


#结果
hello baby~
3

  8.isinstance()   判断对象是否属于某个数据类型

n = 1
print(type(n))
print(isinstance(n,list))  # 判断对象是否属于某个数据类型   #判断n是否属于list类型

 

推荐阅读