pythonyield理解与用法_Python中yield的理解和用法

本文介绍了生成器在Python中的核心概念,它们是一次性的迭代器,节省内存并按需计算。重点讲解了yield的作用,它是函数返回生成器而非普通return,使得函数执行暂停并生成值。通过实例展示了如何利用yield创建无限循环的银行ATM和迭代器工具itertools的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【简单总结】

generator(生成器)

是一次性的iterator(迭代器,例如:list、string、files),但只能遍历迭代一次。好处是,只在迭代(调用)的时候占用临时内存,每一次迭代完成之后,下一次迭代的元素就会重复使用当前使用的内存(上一次迭代的元素被丢弃),这样始终只占用一个元素的内存大小。而不像普通的iterator那样把所有的迭代元素全部放在内存中,虽然可供反复迭代使用,却造成了内存的浪费。

yield

用在函数中,作用和return类似,不同的是yield将返回一个generator,作为函数的返回值。(注意:当你调用函数时,函数中的代码并没有运行,而是返回一个generator对象。)然后,当用这个generator来迭代的时候,将从for循环的首个元素开始,依次迭代(每次都丢弃上一个迭代元素)。

To understand what yield does, you must understand what generators are. And before generators come iterables.

Iterables

When you create a list, you can read its items one by one. Reading its items one by one is called iteration:

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

>>> for i in mylist:

... print(i)

1

2

3

mylist is an iterable. When you use a list comprehension, you create a list, and so an iterable:

>>> mylist = [x*x for x in range(3)]

>>> for i in mylist:

... print(i)

0

1

4

Everything you can use "for... in..." on is an iterable; lists, strings, files...

These iterables are handy because you can read them as much as you wish, but you store all the values in memory and this is not always what you want when you have a lot of values.

Generators

Generators are iterators, a kind of iterable you can only iterate over once. Generators do not store all the values in memory, they generate the values on the fly:

>>> mygenerator = (x*x for x in range(3))

>>> for i in mygenerator:

... print(i)

0

1

4

It is just the same except you used () instead of []. BUT, you cannot perform for i in mygenerator a second time since generators can only be used once: they calculate 0, then forget about it and calculate 1, and end calculating 4, one by one.

Yield

yield is a keyword that is used like return, except the function will return a generator.

>>> def createGenerator():

... mylist = range(3)

... for i in mylist:

... yield i*i

...

>>> mygenerator = createGenerator() # create a generator

>>> print(mygenerator) # mygenerator is an object!

>>> for i in mygenerator:

... print(i)

0

1

4

Here it's a useless example, but it's handy when you know your function will return a huge set of values that you will only need to read once.

To master yield, you must understand that when you call the function, the code you have written in the function body does not run. The function only returns the generator object, this is a bit tricky :-)

Then, your code will continue from where it left off each time for uses the generator.

Now the hard part:

The first time the for calls the generator object created from your function, it will run the code in your function from the beginning until it hits yield, then it'll return the first value of the loop. Then, each other call will run the loop you have written in the function one more time, and return the next value, until there is no value to return.

The generator is considered empty once the function runs, but does not hit yield anymore. It can be because the loop had come to an end, or because you do not satisfy an "if/else" anymore.

Your code explained

Generator:

# Here you create the method of the node object that will return the generator

def _get_child_candidates(self, distance, min_dist, max_dist):

# Here is the code that will be called each time you use the generator object:

# If there is still a child of the node object on its left

# AND if distance is ok, return the next child

if self._leftchild and distance - max_dist < self._median:

yield self._leftchild

# If there is still a child of the node object on its right

# AND if distance is ok, return the next child

if self._rightchild and distance + max_dist >= self._median:

yield self._rightchild

# If the function arrives here, the generator will be considered empty

# there is no more than two values: the left and the right children

Caller:

# Create an empty list and a list with the current object reference

result, candidates = list(), [self]

# Loop on candidates (they contain only one element at the beginning)

while candidates:

# Get the last candidate and remove it from the list

node = candidates.pop()

# Get the distance between obj and the candidate

distance = node._get_dist(obj)

# If distance is ok, then you can fill the result

if distance <= max_dist and distance >= min_dist:

result.extend(node._values)

# Add the children of the candidate in the candidates list

# so the loop will keep running until it will have looked

# at all the children of the children of the children, etc. of the candidate

candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

This code contains several smart parts:

The loop iterates on a list, but the list expands while the loop is being iterated :-) It's a concise way to go through all these nested data even if it's a bit dangerous since you can end up with an infinite loop. In this case, candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) exhausts all the values of the generator, but while keeps creating new generator objects which will produce different values from the previous ones since it's not applied on the same node.

The extend() method is a list object method that expects an iterable and adds its values to the list.

Usually we pass a list to it:

>>> a = [1, 2]

>>> b = [3, 4]

>>> a.extend(b)

>>> print(a)

[1, 2, 3, 4]

But in your code it gets a generator, which is good because:

You don't need to read the values twice.

You may have a lot of children and you don't want them all stored in memory.

And it works because Python does not care if the argument of a method is a list or not. Python expects iterables so it will work with strings, lists, tuples and generators! This is called duck typing and is one of the reason why Python is so cool. But this is another story, for another question...

You can stop here, or read a little bit to see an advanced use of a generator:

Controlling a generator exhaustion

>>> class Bank(): # Let's create a bank, building ATMs

... crisis = False

... def create_atm(self):

... while not self.crisis:

... yield "$100"

>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want

>>> corner_street_atm = hsbc.create_atm()

>>> print(corner_street_atm.next())

$100

>>> print(corner_street_atm.next())

$100

>>> print([corner_street_atm.next() for cash in range(5)])

['$100', '$100', '$100', '$100', '$100']

>>> hsbc.crisis = True # Crisis is coming, no more money!

>>> print(corner_street_atm.next())

>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs

>>> print(wall_street_atm.next())

>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty

>>> print(corner_street_atm.next())

>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business

>>> for cash in brand_new_atm:

... print cash

$100

$100

$100

$100

$100

$100

$100

$100

$100

...

Note: For Python 3, useprint(corner_street_atm.__next__()) or print(next(corner_street_atm))

It can be useful for various things like controlling access to a resource.

Itertools, your best friend

The itertools module contains special functions to manipulate iterables. Ever wish to duplicate a generator? Chain two generators? Group values in a nested list with a one-liner? Map / Zipwithout creating another list?

Then just import itertools.

An example? Let's see the possible orders of arrival for a four-horse race:

>>> 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)]

Understanding the inner mechanisms of iteration

Iteration is a process implying iterables (implementing the __iter__() method) and iterators (implementing the __next__() method). Iterables are any objects you can get an iterator from. Iterators are objects that let you iterate on iterables.

There is more about it in this article about how for loops work.

yield函数是一个生成器(generator),它可以用于迭代。在函数中,yield类似于return,不同的是,yield返回一个值并且记住这个返回值的位置,下次迭代就从记住的位置开始执行,并且从上一次迭代遇到的yield后面的代码开始执行。 yield函数的优点在于它可以减少内存的消耗,因为它不直接生成返回值。相比使用return返回值或者迭代大量数据,yield函数能够更好地减少内存消耗。然而,由于yield函数只能读取一次,所以它的特点是只能迭代一次。 yield函数不仅可以用于for循环,还可以作为函数参数。例如,可以将yield函数作为其他函数的参数传递,实现更加灵活的功能。 除了next()函数之外,还有一个特别的函数叫做send()函数。send()函数的特点是可以携带参数,并且可以修改上一个yield表达式的值。使用方式next()函数有很多相似之处。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span> #### 引用[.reference_title] - *1* *2* *3* *4* [Python|yield的解析及用法](https://blog.youkuaiyun.com/gschen_cn/article/details/107293784)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值