我在Python 3.2中有一个代码,我想在Python 2.7中运行它.我确实转换了它(已经把missing_elements
两个版本的代码都放了)但我不确定这是否是最有效的方法.基本上如果yield from
在missing_element
功能的上半部分和下半部分有两个如下所示的调用会发生什么?两个部分(上部和下部)中的条目是否在一个列表中相互附加,以便父级递归函数与yield from
调用一起使用并将两个部分一起使用?
def missing_elements(L, start, end): # Python 3.2
if end - start <= 1:
if L[end] - L[start] > 1:
yield from range(L[start] + 1, L[end])
return
index = start + (end - start) // 2
# is the lower half consecutive?
consecutive_low = L[index] == L[start] + (index - start)
if not consecutive_low:
yield from missing_elements(L, start, index)
# is the upper part consecutive?
consecutive_high = L[index] …
Run Code Online (Sandbox Code Playgroud) 我的理解yield from
是,它类似于yield
从可迭代对象中获取每个项目。然而,我在以下示例中观察到不同的行为。
我有Class1
class Class1:
def __init__(self, gen):
self.gen = gen
def __iter__(self):
for el in self.gen:
yield el
Run Code Online (Sandbox Code Playgroud)
和 Class2 的不同之处仅在于yield
将 for 循环替换为yield from
class Class2:
def __init__(self, gen):
self.gen = gen
def __iter__(self):
yield from self.gen
Run Code Online (Sandbox Code Playgroud)
下面的代码从给定类的实例中读取第一个元素,然后在 for 循环中读取其余元素:
a = Class1((i for i in range(3)))
print(next(iter(a)))
for el in iter(a):
print(el)
Run Code Online (Sandbox Code Playgroud)
Class1
这会为和产生不同的输出Class2
。对于Class1
输出是
0
1
2
Run Code Online (Sandbox Code Playgroud)
Class2
输出为
0
Run Code Online (Sandbox Code Playgroud)
yield from
产生不同行为的背后机制是什么?
我有一个Connection
用于包含读取和写入asyncio
连接流的对象:
class Connection(object):
def __init__(self, stream_in, stream_out):
object.__init__(self)
self.__in = stream_in
self.__out = stream_out
def read(self, n_bytes : int = -1):
return self.__in.read(n_bytes)
def write(self, bytes_ : bytes):
self.__out.write(bytes_)
yield from self.__out.drain()
Run Code Online (Sandbox Code Playgroud)
在服务器端,每次客户端连接时connected
创建一个Connection
对象,然后读取4个字节.
@asyncio.coroutine
def new_conection(stream_in, stream_out):
conn = Connection(stream_in, stream_out)
data = yield from conn.read(4)
print(data)
Run Code Online (Sandbox Code Playgroud)
在客户端,写出4个字节.
@asyncio.coroutine
def client(loop):
...
conn = Connection(stream_in, stream_out)
yield from conn.write(b'test')
Run Code Online (Sandbox Code Playgroud)
这个作品几乎为预期的,但我必须yield from
每天read
和write
电话.我yield from
从里面尝试过Connection
: …
在包装(内部)迭代器时,通常必须将__iter__
方法重新路由到底层的iterable.请考虑以下示例:
class FancyNewClass(collections.Iterable):
def __init__(self):
self._internal_iterable = [1,2,3,4,5]
# ...
# variant A
def __iter__(self):
return iter(self._internal_iterable)
# variant B
def __iter__(self):
yield from self._internal_iterable
Run Code Online (Sandbox Code Playgroud)
变体A和B之间是否有任何显着差异?Variant A返回iter()
已从内部iterable中查询过的迭代器对象.变量B返回一个生成器对象,该对象返回内部可迭代的值.出于某种原因,是其中一种还是其他的?在collections.abc
该yield from
版本中使用.该return iter()
变种是,我已经使用到现在的格局.
在Python中,大多数收益率的例子都说明了这一点
yield from foo()
Run Code Online (Sandbox Code Playgroud)
类似于
for x in foo(): yield x
Run Code Online (Sandbox Code Playgroud)
另一方面它似乎并不完全相同,并且有一些魔法投入.我觉得使用一个我不理解的魔法函数有点不安.我有什么必须知道的,yield from
以避免陷入魔法做我不期望的事情的魔力?我应该注意到魔法提供了哪些优势?
我有一个功能,返回一个发电机.目前它使用yield from
:
function foo()
{
$generator = getGenerator();
// some other stuff (no yields!)
yield from $generator;
}
Run Code Online (Sandbox Code Playgroud)
如果我yield from
用一个简单的替换它,return
在这种情况下,这会改变什么吗?也许在执行中?还是表现?会yield from
产生一个新的"外部"迭代器吗?
我知道,在其他情况下yield from
可以更灵活,因为我可以使用它几次甚至将它与简单的yield
s 混合,但这对我的情况无关紧要.
更新:我已经启动了一个关于 python-ideas的线程来为此目的提出额外的语法或 stdlib 函数(即指定由 发送的第一个值yield from
)。到目前为止 0 回复...:/
如何拦截子生成器的第一个生成值,但使用 将迭代的其余部分委托给后者yield from
?
例如,假设我们有一个任意的双向生成器subgen
,我们想将它包装在另一个生成器中gen
。的目的gen
是拦截第一个subgen
生成的值,并将生成的其余部分(包括发送的值、抛出的异常、.close() 等)委托给子生成器。
可能会想到的第一件事可能是这样的:
def gen():
g = subgen()
first = next(g)
# do something with first...
yield "intercepted"
# delegate the rest
yield from g
Run Code Online (Sandbox Code Playgroud)
但这是错误的,因为当调用者.send
在获得第一个值后将某些东西返回给生成器时,它最终会作为yield "intercepted"
表达式的值被忽略,而是作为第一个值g
接收,作为语义的一部分的。None
.send
yield from
所以我们可能会想这样做:
def gen():
g = subgen()
first = next(g)
# do …
Run Code Online (Sandbox Code Playgroud) 假设我有这些解析器:
parsers = {
".foo": parse_foo,
".bar", parse_bar
}
Run Code Online (Sandbox Code Playgroud)
parse_foo
并且parse_bar
都是逐个产生行的生成器.如果我想创建一个调度函数,我会这样做:
def parse(ext):
yield from parsers[ext]()
Run Code Online (Sandbox Code Playgroud)
语法的收益使我能够轻松地在生成器上下传递信息.
有没有办法在修改产量结果的同时保持隧道效应?
在破坏隧道时这样做很容易:
def parse(ext):
for result in parsers[ext]():
# Add the extension to the result
result.ext = ext
yield result
Run Code Online (Sandbox Code Playgroud)
但这种方式我无法使用.send()
或.throw()
一直到解析器.
我想到的唯一方法是做一些丑陋的事情try: ... except Exception: ...
并将异常传递出去,同时做同样的事情.send()
.它很难看,很杂乱,容易出错.
最初(PEP 380),yield from
引入了语法以用于委托给“子生成器”。后来它与现在已弃用的基于生成器的协程一起使用。
我不知道yield from
一般可以应用什么样的对象。我的第一个猜想是它只需要__iter__
对象上的方法来返回迭代器。实际上,以下适用于 Python 3.8:
class C:
def __init__(self, n):
self.n = n
def __iter__(self):
return iter(range(self.n))
def g(n):
yield from C(n)
print(tuple(g(3)))
Run Code Online (Sandbox Code Playgroud)
但是,它也适用于一些asyncio.sleep(1)
没有__iter__
方法的等待对象,例如。
一般规则是什么?是什么决定了一个对象是否可以作为yield from
form的参数给出?
是否可以嵌套yield from
语句?
简单的形式是显而易见的:
def try_yield1():
x = range(3)
yield from x
Run Code Online (Sandbox Code Playgroud)
哪个产生:
0
1
2
Run Code Online (Sandbox Code Playgroud)
但是,如果我有嵌套的生成器怎么办?
def try_yield_nested():
x = [range(3) for _ in range(4)]
yield from ((yield from y) for y in x)
Run Code Online (Sandbox Code Playgroud)
这会产生:
0
1
2
None # why?
0
1
2
None # ...
0
1
2
None # ...
Run Code Online (Sandbox Code Playgroud)
None
如果我使用yield from
它(为什么它是嵌套的)它为什么会产生?
我知道我可以这样做:
from itertools import chain
def try_yield_nested_alternative():
x = [range(3) for _ in range(4)]
yield from chain.from_iterable(x)
Run Code Online (Sandbox Code Playgroud)
这产生相同的输出而忽略了None
(这是我期望的).我也可以写一个简单的循环: …
yield-from ×10
generator ×9
python ×9
python-3.x ×6
yield ×4
coroutine ×1
iterable ×1
php ×1
python-2.x ×1
return ×1