要理解yield的作用,必须先理解什么是生成器。另外,在理解生成器之前,你必须理解_iterables_。Iterable:Iterable创建了一个列表,自然需要能够逐一读取每一个元素。逐项读取其项目的过程称为迭代:>>>mylist=[1,2,3]>>>foriinmylist:...print(i)123mylist是可迭代的。当你使用列表理解时,你正在创建一个列表,因此是可迭代的:>>>mylist=[x*xforxinrange(3)]>>>foriinmylist:...print(i)014All可用于...in...的数据结构是可迭代的;列表、字符串、文件...这些可迭代方法很方便,因为您可以随意读取它们,但是您将所有值存储在内存中,当您有很多值时,这并不总是需要的。Generator:生成器generator也是迭代器的一种,一种特殊的迭代,只能迭代一次。生成器不会将所有值存储在内存中,而是动态生成值:generator:generator,generator,generatorsgenerateelectricitybutdon'tstoreenergy;)>>>mygenerator=(x*xforxinrange(3))>>>foriinmygenerator:...print(i)014只要使用()而不是[],列表推导就变成了生成器推导。然而,由于一个生成器只能使用一次,所以你不能对foriinmygenerator做第二次:生成器计算0,然后丢弃它,然后计算1,最后计算4。一个典型的盲人打碎玉米。yield:yieldyield关键字的使用方式与return相同,只是该函数将返回一个生成器。>>>defcreateGenerator():...mylist=range(3)...foriinmylist:...yieldi*i...>>>mygenerator=createGenerator()#创建一个生成器>>>print(mygenerator)#mygeneratorisanobject!>>>foriinmygenerator:...print(i)014这个例子本身没什么用,但是当你需要一个函数返回很多的时候ofvalues并且只需要读取时取一次,用yield就方便了。掌握yield,有一点需要明确:调用函数时,写在函数体中的代码不会运行,函数只返回generator对象,初学者容易混淆。第二点要理解的是,每次for使用生成器时,代码都会从它停止的地方继续。现在是困难的部分:第一次调用从您的函数创建的生成器对象时,它将从头开始运行函数中的代码,直到遇到yield,然后它将返回循环的第一个值。然后,每个后续调用将运行您在函数中编写的循环的下一次迭代,返回下一个值。这将一直持续到生成器被认为是空的,这发生在函数运行时没有yield的情况下。那可能是因为循环已经结束,或者因为您不再满足“if/else”。yield:produce,surrender,yield,yield,giveway用代码来说明生成器is会被调用一次:#如果还有左子对象节点,并且距离合适,则返回下一个子对象ifself._leftchildanddistance-max_dist=self._median:yieldself._rightchild#函数执行到这里,认为生成器为空#这里的值最多只有两个:left和rightsub-objectcallercaller:#创建一个空列表,和一个包含当前对象引用结果的列表,candidates=list(),[self]#循环candidates(开头只有一个元素)whilecandidates:#取最后一个candidate,从列表中移除node=candidates.pop()#获取obj和candidate之间的距离distance=node._get_dist(obj)#如果距离合适,填入结果ifdistance<=max_distanddistance>=min_dist:result.extend(node._values)#并将candidate的子元素添加到列表中#循环直到candidate的所有子元素的子元素都被锁定candidates.extend(node._get_child_candidates(distance,min_dist,max_dist))returnresult此代码包含几个智能部分:对于列表循环会迭代,但循环会在迭代时扩展列表:-)这是遍历所有这些嵌套数据的一种巧妙方法,即使它有点危险,因为在这种情况下你可能会遇到无限循环,请候选人。extend(node._get_child_candidates(distance,min_dist,max_dist))穷尽了所有生成器的值,但是在继续创建新的生成器对象的同时,由于它们不是应用于同一个节点,它们会产生与以前的。extend()方法是一个列表对象方法,它需要一个可迭代对象并将其值添加到列表中。通常我们传递一个列表:>>>a=[1,2]>>>b=[3,4]>>>a.extend(b)>>>print(a)[1,2,3,4]但是在上面的代码中,你得到了一个生成器,这更好,因为:不需要两次读取值。可能有很多子元素,你不希望它们都存储在内存中。这是有效的,因为Python不关心方法的参数是否是列表。Python需要可迭代对象,因此它适用于字符串、列表、元组和生成器!这称为鸭子类型,这是Python如此酷的原因之一。但那是另一回事了……控制生产者的疲惫>>>classBank():#找一家银行,弄几台自动取款机……crisis=False……defcreate_atm(self):..whilenotself.crisis:...yield"$100">>>hsbc=Bank()#如果一切顺利,你想要多少ATM,它会给你多少>>>corner_street_atm=hsbc.create_atm()>>>print(corner_street_atm.next())$100>>>print(corner_street_atm.next())$100>>>print([corner_street_atm.next()forcashinrange(5)])['$100','$100','$100','$1??00','$100']>>>hsbc.crisis=True#危机来了,没钱了!>>>print(corner_street_atm.next())>>>wall_street_atm=hsbc.create_atm()#新ATM同样>>>print(wall_street_atm.next())>>>hsbc.crisis=False#危机后ATM中没有钱>>>print(corner_street_atm.next())>>>brand_new_atm=hsbc.create_atm()#Makeanewnewstudent>>>换现金brand_new_atm:...printcash$100$100$100$100$100$100$100$100$100...注意:对于Python3,使用print(corner_street_atm.__next__())或print(next(corner_street_atm))来控制对资源的访问This可以用于Itertools之类的各种东西,你最好的朋友itertools模块包含用于操作可迭代对象的特殊函数。曾经想复制发电机吗?链接两个发电机?用一个衬里嵌套列表中的嵌套值?Map/Zip没有创建另一个列表?然后导入itertools。一个例子?让我们看看四匹马的可能到达顺序:>>>horses=[1,2,3,4]>>>races=itertools.permutations(horses)>>>print(races)>>>print(list(itertools.permutations(horses)))[(1,2,3,4),(1,2,4,3),(1,3,2,4),(1,3,4,2),(1,4,2,3),(1,4,3,2),(2,1,3,4),(2,1,4,3),(2,3,1,4),(2,3,4,1),(2,4,1,3),(2,4,3,1),(3,1,2,4),(3,1,4,2),(3,2,1,4),(3,2,4,1),(3,4,1,2),(3,4,2,1),(4,1,2,3),(4,1,3,2),(4,2,1,3),(4,2,3,1),(4,3,1,2),(4,3,2,1)]理解迭代的内部机制迭代是一个隐含的可迭代对象(实现了__iter__()方法)和迭代器(实现了__next__()方法)的过程。可迭代对象是可以从中获得迭代器的任何对象。迭代器是允许您迭代可迭代对象的对象。