1、容器(container)英 [kənˈteɪnə(r)]

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in,not in关键字判断元素是否包含在容器中。

例子:
ret = [1,2,3]
print(1 in ret) # True

(1)常见的容器对象:str(字符串)、list(列表)、tuple(元组)、dict(字典)、set(集合)

(注 :尽管大多数容器都提供了某种方式来获取其中的每一个元素,但这并不是容器本身,而是可迭代对象赋予了容器这种能力,当然并不是所有容器都是可迭代的)

2、可迭代对象(iterable:可迭代的)===和容器一样是通俗的叫法

(1)只要含有__iter__方法都是可迭代的

例如:str、list、tuple、dict、f=open()、range()、enumerate枚举

判读方法中是否含有__iter__方法:例子

(说明:dir() 带参数时,返回参数的属性、方法列表)

print('__iter__' in dir(str))
print('__iter__' in dir(list))
print('__iter__' in dir(tuple))
print('__iter__' in dir(dict))
print('__iter__' in dir(range(10)))
print('__iter__' in dir(open('py.py',encoding='utf-8')))
print('__iter__' in dir(enumerate([])))
# True

# 查看共有方法
ret= set(dir([]))&set(dir({}))&set(dir(''))&set(dir(range(10)))
print(ret)

(2)只要能被for循环的数据类型想,就一定拥有__iter__方法,可迭代的

3、迭代器(iterator)

(1)迭代器是一个带状态的对象,它能在你调用__next__方法的时候返回容器中的下一个值,任何实现了__iter__和__text__方法的对象都是迭代器,__iter__返回迭代器本身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常。

例子:

print([].__iter__())
# 一个列表执行了__iter__()之后的返回值就是一个迭代器


l = [1,2,3]
iterator = l.__iter__()
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
#1
#2
#3
#Traceback (most recent call last):
  File "E:/wj/慕课/capabilities/py.py", line 55, in <module>
    print(iterator.__next__())
StopIteration

(2)iterable 可迭代的   ----->__iter__   # 只要含有__iter__方法的都是可迭代的

(3)[].__iter__() 迭代器  ----->__next__  # 通过next就可以从迭代器中一个个的取值

(4)只要含有__iter__方法的都是可迭代的-----可迭代协议

(5)迭代器的好处:

                # 从容器类型中一个一个的取值,会把所有的值都取到。

                # 节省内存空间

                         # 迭代器并不会再内存中占用一大块内存,(已经存在的就不节省内存了,如:list;range节省内存空间)

                                         # 而是随着循环,每次生成一个

                                         # 每次next每次给我一个

4、生成器(generator):

(1)为什么要用生成器?

我们知道可以用列表存储数据,可是当我们的数据特别大的时候建立一个列表的存储数据会很占内存的,

这个时候生成器就用到了,它可以说是不怎么占用计算机资源的一种方法。

(2)生成器函数--本质上就是自己写的函数(生成器本身是自己写的迭代器)

例子 生成器表达式:

def generator():
    print(1)
    yield 'a'

ret=generator()   #  得到的是生成器,不会执行函数内的代码
print(ret)
print(ret.__next__())
# <generator object generator at 0x000000000214B7C8>
# 1
# a

(注:

              (1)只要含有yield关键字的函数都是生成器函数;

              (2)yield不能和return共用且需要写在函数内;

              (3)生成器函数:执行之后会得到一个生成器作为返回值。)

(3)迭代器:对于列表for循环而言,多次for循环,列表只是可迭代的对象,for循环的过程中转换成的是新的迭代器,所以每次用新的迭代器。

(4)生成器函数:

例子:生成器函数循环打印生成器信息

def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'
ret=generator()
print(ret.__next__())  # 执行生成器函数
print(ret.__next__())  # 执行生成器函数

例子2:生成器函数打印多次调用,接着上次的打印

def wahaha():
    for i in range(2000):
        yield '娃哈哈%s'%i
g = wahaha()
g1 =wahaha()
print(g.__next__())
print(g1.__next__())


g=wahaha()
count=0
for i in g:
    count=+1
    print(i)
    if count>50:
        break
print('*********',g.__next__())
for i in g:
    count=+1
    print(i)
    if count>100:
        break

例子3:监听器文件输入的例子

def tail(filename):
    f=open(filename,encoding='utf-8')
    while True:
        line=f.readline()
        if line.strip():
            yield line.strip()

g = tail('app.py')
for i in g:
    if 'python' in i:
        print('***',i)

5、复习迭代器(iterator)和生成器(generator)

迭代器和生成器:

迭代器:

(1)双下方法:很少直接调用的方法,一般情况下,是通过其它语法触发的

(2)可迭代的--可迭代协议,含有__iter__方法(‘__iter__’ in dir(数据))

(3)可迭代的一定可以for循环

(4)迭代器协议:含有__iter__和__next__方法

(5)迭代器一定是可迭代的,可迭代的通过调用iter()方法就能得到一个迭代器

(6)迭代器的特点:

                 # 很方便使用,且只能取所有的数据取一次

                # 节省内存空间

生成器:

(1)生成器的本质就是迭代器

(2)生成器的表现形式:

            # 生成器函数

           #  生成器表达式

l = [1,2,3,4]
for i in l:
    print(i)
    if i==2:
        break
for i in l:
    print(i)

(3)生成器函数:

            # 含有yield关键字的函数就是生成器函数

            # 特点:

                       # 调用函数之后函数不执行,返回一个生成器

                       # 每次调用next方法的时候会取一个值

                      # 直到去完最后一个,再执行next报错

(4)从生成器中取值的方法:

# next

# for

# 数据类型强制转换:占用内存

(5)例子:

def generator():
    for i in range(20):
        yield '娃哈哈%s'%i
g = generator()  # 调用生成器函数得到一个生成器,不会执行函数体中的内容
# print(list(g)) # 强制转换生成器为列表
ret = g.__next__()  # 每次执行g.__next__就从生成器中取值,预示着生成器函数中的代码继续执行
print(ret)

num=0
for i in g:
    num+=1
    if num>10:
        break
    print(i)

=============================生成器(generator)函数进阶(难点)==================================

一、yield的使用:

1)函数中使用yield,可以使函数变成生成器,一个函数如果是生成一个数组,就必须把数据存储在内存中,如果使用生成器,则在调用的时候才能生成数据,可以节省内存。

2)生成器方法调用时,不会立即执行 ,需要调用next()或使用for循环来执行。使用for循环不需要自己捕获StopIteration异常。使用next()方法,当生成器方法执行结束后会抛出异常(只要不是使用yield返回数据,都会抛出StopIteration异常)

1、生成器(generator):send方法例子:

def generator():
    print(123)
    content=yield 1
    print("=====",content)
    print(456)
    content=yield 4
    print(content)
    yield
# g1 = generator()
# g1.__next__()
# g1.__next__()
# print("***",g1.__next__())
# print("***",g1.__next__())

g = generator()
ret = g.__next__()
print('***',ret)
ret1 = g.send('hello') # send的效果和next方法一样,只不过,上一个yield位置的变量
# 可以接收send的值
print("***",ret1)

容器的pod 英文全拼 容器的单词_迭代

总结:send获取下一个值的效果和next基本一致

只是在获取下一个值的时候,给上一个yield的位置传递一个数据

使用send注意事项:

# 第一次使用生成器的时候,是用next获取下一个值
              # 最后一个yield不能接受外部的值

2、生成器(generator)进阶例子:

获取移动的平均值:射击/余额宝利息(生成器generator和send合适)====有分析步骤

#获取移动的平均值:射击/余额宝利息(生成器generator和send合适)
#10 20 30
#10 15 20

#分析步骤
#avg = sum/count
# sum = 0
# count = 0
# avg = 0
"""
#只能计算一个平均数
def averge():
    sum = 0
    count = 0
    avg = 0
    num = yield   #接收外面的值num
    sum += num
    count += 1
    avg = sum/count
    yield avg
avg_g = averge()
avg_g.__next__()
avg1 = avg_g.send(10)
print(avg1)
"""
"""
def averge():
    sum = 0
    count = 0
    avg = 0
    while True:
        num = yield   #接收外面的值num
        sum += num
        count += 1
        avg = sum/count
        yield avg
avg_g = averge()
avg_g.__next__()
avg1 = avg_g.send(10)
print(avg1)   #重新循环都要先next再send(),next没有逻辑
"""
"""
def averge():
    sum = 0
    count = 0
    avg = 0
    while True:
        # num = yield   #接收外面的值num
        sum += num
        count += 1
        avg = sum/count
        num = yield avg
avg_g = averge()
avg_g.__next__()
avg1 = avg_g.send(10)
print(avg1)

"""

def averge():
    sum = 0
    count = 0
    avg = 0
    while True:
        # num = yield   #接收外面的值num
        num = yield avg
        sum += num
        count += 1
        avg = sum/count

avg_g = averge()
avg_g.__next__()
avg1 = avg_g.send(10)
avg2 = avg_g.send(30)
avg3 = avg_g.send(30)
print(avg1)
print(avg2)
print(avg3)

============================生成器表达式和列表推导式============================

# 1、列表推导式:
li = ['鸡蛋%s'%i for i in range(10)]
print(li)
# 2、生成器表达式
g = ('鸡蛋%s'%i for i in range(10))
print(g)

总结:

生成器表达式和列表推导式的区别:

括号不一样,返回值不一样,生成器表达式几乎不占内存

2、各种推导式总结:

(1)[每个元素或者是和元素相关的的操作  for  元素  in 可迭代的类型]

#  遍历后挨个处理

(2)[满足条件的元素相关的操作  for  元素  in  可迭代的数据类型  if  元素相关的条件]   

# 筛选功能

例子:

# 30以内所有能被3整除的数
li = [i for i in range(30) if i%3==0]   # 完整的列表推导式
print(li)


# 例三:找到嵌套列表中名字含有两个‘e’的所有名字
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
       ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
li = [i for j in names for i in j if i.count('e')==2 ]
print(li)


# 4、字典推导式
# (1)例子1:将一个字典的key和value对调
dic_1 = {'a':10,'b':30}
dic_2 = {dic_1[k]:k for k in dic_1}
print(dic_2)


# (2)例二:合并大小写对应的value值,将k统一成小写
dic_1 = {'a':10,'b':34,'A':7,'Z':3}

# 集合推导式,自带结果去重功能
set_01 = {x**2 for x in [1,-1,2]}
print(set_01)
# {'a':10+7,'b':34,'z':3}


# 第一种方法:
dic_2 =  {}
for key in dic_1:
    value = dic_1.get(key.lower(),0)+dic_1.get(key.upper(),0)
    print(value)
    dic_2.setdefault(key.lower(),value)
print(dic_2)


# 第二种:
dic_1 = {'a':10,'b':34,'A':7,'Z':3}
dic_2 = {k.lower():dic_1.get(k.lower(),0)+dic_1.get(k.upper(),0) for k in dic_1}
print(dic_2)