Python之 反射、迭代器、生成器

一、反射

反射就是通过 内置函数getattr() 以字符串的形式导入模块以字符串的形式调用模块/对象里方法

l=['add','del','set','find']
for i in l:
    print(i)
choice=input("请输入您需要的操作:".strip())
mode=__import__(choice)    #相当于 import 模块
getattr(mode,choice[0])()  #获取  模块里定义的方法(函数)并且执行;

二、迭代器

Python的数据类型分为

不可迭代类型(数字、布尔值)

可迭代类型(字符串、列表、元组、集合、字典、文件句柄、range、枚举....),

这些可迭代对象有一个共同的特征就是可以被for循环,而不能被for循环的都是不可迭代对象;

那么他们为什么可以被for循环呢?

Python种一切皆对象体现在+/-....语法都可以被Python解释隐藏调用一些双下(__add__。。。。)方法也就是已经被C语言实现好了的方法,当解释器遇到语法就会调用其对应的双下方法,进而返回结果;

顺腾摸瓜我们就来看一下这些可迭代对象中都隐藏了什么__共同的双下___方法;

ret=set(dir({})) & set(dir([])) & set(dir(()))
print(ret)

{
'__reduce__', '__eq__', '__reduce_ex__', '__getitem__', '__len__', '__sizeof__', '__getattribute__',

'__format__', '__subclasshook__', '__delattr__', '__gt__', '__setattr__', '__hash__', '__contains__',

'__class__', '__iter__', '__doc__', '__le__', '__new__', '__init_subclass__', '__repr__', '__ge__',

'__lt__', '__ne__', '__dir__', '__str__', '__init__'

}
print('__iter__' in set(dir({})))      #可迭代对象拥有__iter__双下方法
print('__iter__' in set(dir(True)))   #不可迭代对象没有__iter__双下方法

得知所有的可迭代对象内部都有1个__inter__()双下方法,这就是Python中规定的可迭代协议

所以在for循环执行之前会去寻找循环对象的__inter__方法,如果没有__inter__方法,循环会直接报错,这是不可以迭代的对象;

l1=[1,2,3,4]
print(l1.__iter__())

 所有可迭代对象执行__iter__双下方法之后都会返回1个迭代器

l1=[1,2,3,4]
print(set(dir(l1.__iter__())) - set(dir({})))

#{'__setstate__', '__next__', '__length_hint__'}

迭代器可迭代对象多了3个双下方法分别是:__setstate__, __next__, __length_hint__

class A():
    def __iter__():pass
    def __next__():pass

a=A()    

from collections import Iterable     
from collections import Iterator        

print(isinstance(a,Iterable))#可迭代对象
print(isinstance(a,Iterator))#迭代器

迭代器协议:实现了__iter__ + __next__方法就是一个迭代器;

 迭代器的作用:

1.由于可迭代对象生成数据是不会一下生成全部的数据,而是生成一个迭代器,然后1个1个得吐给你,所以就避免可生成大数据导致内存撑爆;

generater=range(10000000000000000000000000)        #迭代器把数字1个1个得吐出来,不会占用1大块内存,而是随着for循环/__next__()1个1个得生成;
for n in generater: #随着for循环1个1个得取处迭代器中值
    print(n)

#big_data=list(range(10000000000000000000000000)) #会把数据1次生成,如果数据量过大,容易沾满内存

2.for循环是 对可迭代协议的实现

为啥列表/字典这些可迭代对象,没有__next__方法,容器中的元素也可以被 for循环出来呢?

因为for循环先执行了 [].__iter()__()把可迭代对象中__iter__()方法returen的结果组成 1个迭代器

然后1次1次得执行 [].__iter()__()__next__()把元素1个1个得获取出来。

#Python中for 循环的原理
list1=['1','2','3','4','5']
g=list1.__iter__()  #第1步:可迭代对象执行__iter__()得到1个生成器g
print(g.__next__()) #第2步:生成器g.__next__()遍历数据
print(g.__next__())
print(g.__next__())
print(g.__next__())

迭代器的应用场景

在平时写代码的过程中,需要产生大量的数据,但1次性全部生成又会导致内存占用量大,就可以使用迭代器协议,先生成迭代器,然后1个1个得吐出数据;

三、生成器

生成器的本质还是迭代器,可不过这个迭代器是程序员自己实现的

实现生成器有2种方式:

方式1:yield写生成器函数

def func():                #普通函数
    return 'zhanggen'

ret=func()
print(ret)
------------------------------------------------------------------
def generator():      #0.生成器函数:函数内部包含yield关键字,就是生成器函数
    yield 'zhang'
    yield 'gen'

g= generator()        #1.生成器函数被调用后,得到1个生成器也就是迭代器作为返回值。
print(g.__next__())   #2.使用__next__()从里面取值
print(g.__next__())

使用生成器生成1000000个哇哈哈哈,只获取前50个

def wahaha():
    for i in range(1000000):
        yield "wahaha%s" %i
        
        
g=wahaha()
print (g.__next__())#wahaha0

count=0            #获取前50个
for i in g:
    count+=1
    if count<=50:
        print('for循环....%s'%i)

 监听文件的输入,然后过滤用户输入的关键字

 

方式2:生成器表达式

g=(i for i in range(1,100))

print(g.__next__())

 四、生成器进阶

send语法

send的作用和next类似,不同的是send可以在获取生成器函数里面 下1个yield 值的同时,还可以在上1个yield 值的右边send1个值到生成器函数里面。

def generator():
    print(123)
    num=yield 1  #Python解释器当遇到1个等号时 先执行右边的yield 1,然后赋值num=yield 1
    print('send传入的值',num)
    print(456)
    num1=yield 2
    print(num1)
    yield 3
g=generator()
print(g.__next__())   #第一次调用生成器时不能使用 send
print(g.send('hello'))#send的效果和__next__效果一样
print(g.send('hello1'))#send的效果和__next__效果一样
print(g.__next__())#最后1次调用生成器不使用 send

使用send的注意事项:

综上所述send的功能,send不可以 第1次和最后1次调用生成器时使用。

如何让1个生成器,无限的yield值 ,不会遇到  StopIteration异常。

def average():
    print('start')
    num=0
    while True:#下次调用 __next__依然在while循环里面
        num+=4
        yield num #4 8 12 16... 


g=average()

print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())

使用sen不断求平均值

#不断得计算平均值
def average():
    sum_=0
    count_=0
    avg_=0
    while True:
        num_=yield avg_
        sum_+=num_
        count_+=1
        print(sum_,num_,count_)
        avg_=sum_/count_

avg_g=average()
avg_g.__next__()
print(avg_g.send(10))
print(avg_g.send(12))
print(avg_g.send(15))
print(avg_g.send(16))

 yield from 语法

yield from 帮助我们从序列数据类型里1个1个得yield 出每1个元素,而不是使用 for 循环

def generator():
    a='abcde'
    b='12345'
    yield  a
    yield b
'''
abcde
12345
'''
g=generator()
for i in g:
    print(i)

def generator():
    a='abcde'
    b='12345'
    yield from a  #帮助我们从序列数据类型里,1个1个yield 出每个元素,而不是使用 for 循环。
    yield from b

g=generator()
for i in g:
    print(i)

'''
a
b
c
d
e
1
2
3
4
5

'''

chain多个生成器

def coroutine1(n):
    print('我这coroutine1里面')
    yield '1'
    yield '2'

def coroutine2(n):
    print('我这coroutine2里面')
    yield '3'
    yield '4'

def func(n): print('我在func里面') yield from coroutine1(n) #yield from 相当于1个中间件,可以在1个函数里面 直接 把其他生成器里所有yield的值获取到 yield from coroutine2(n) g=func(2) print(next(g)) print(next(g)) print(next(g)) print(next(g)) ''' 我这coroutine1里面 1 2 我这coroutine2里面 3 4 '''

yield from新语法

参考

原文地址:https://www.cnblogs.com/sss4/p/6683001.html