Python 3.3에서 생성기에는 새로운 구문인 Yield from이 있습니다. Yield from의 기능은 무엇인가요? 구문은 무엇입니까? 다음 글은 Python 3의 구문에서 나오는 Yield 관련 정보를 위주로 자세하게 소개하고 있으니, 필요하신 분들은 참고하시면 좋을 것 같습니다. 잘못된 구문을 직접 보고합니다. 언뜻 보면 2to3 처리에 문제가 있는 줄 알았습니다. 비난할 수는 없습니다. 뭐~ 많은 패키지가 2로 작성되었다가 3으로 변환됩니다. 알고 보니 asyncio만 지원하는 것으로 나타났습니다. 3.3+.버전에서 코드를 다시 보다가 갑자기
라는 문장을 발견했습니다. 그런데 는 마법의 말입니다.
PEP-380pip install asyncio
yield from
yield
글쎄, 구글을 통해 이 제목을 떠올렸습니다. yield from
의 과거와 현재의 삶이 모두 이 PEP에 담겨 있습니다. 일반적인 아이디어는 원래 아이디어입니다
이 있었습니다. PEP-380은
의 의미, 즉 중첩된 생성기가 가져야 하는 동작 패턴을 지정합니다.yield from
함수 A에 이러한 명령문이 있다고 가정합니다. yield
yield from
yield from B()
yield from
는 반복 가능한(반복 가능한) 객체 b를 반환한 다음 A() 생성기를 반환합니다. 명명 규칙에 따르면 이름은 a입니다. 그러면
b 반복으로 생성된 각 값은 a의 호출자에게 직접 전달됩니다.
B()
을 실행할 때 a에서 예외가 발생하게 됩니다.
a에 GeneratorExit 이외의 예외가 발생하면 해당 예외는 b에 직접 발생합니다. b의 throw 메서드가 StopIteration을 발생시키는 경우 a는 계속해서 실행되고 다른 예외로 인해 a도 예외가 발생하게 됩니다. __next__()
yield from
yield from
예외를 발생시키므로 b의 return 값은 a의 yield from
식의 반환 값이 됩니다.
return <expr>
쉔마는 왜 이렇게 요구사항이 많은가요? throw 메서드를 추가하면 생성기의 동작이 매우 복잡해지기 때문에, 특히 여러 생성기가 함께 있는 경우 이를 작동하려면 프로세스 관리와 유사한 기본 요소가 필요합니다. 위의 모든 요구 사항은 본질적으로 복잡한 생성기 동작을 통합하기 위한 것이므로 당연히 단순화할 수 없습니다. StopIteration(<expr>)
yield from
PEP 작성자가 말하려는 내용을 이해하지 못했음을 인정하므로 PEP를 "재구성"하는 것이 도움이 될 수 있습니다.
def inner(): coef = 1 total = 0 while True: try: input_val = yield total total = total + coef * input_val except SwitchSign: coef = -(coef) except BreakOut: return total
함수로 얻은 생성기는 send를 통해 작업을 위한 데이터를 수신하는 동시에 throw 메서드를 통해 외부 코드로부터 제어를 받아 다양한 코드 분기를 실행합니다. 지금까지는 매우 명확합니다.
다음으로 요구사항 변경으로 인해
코드 앞뒤에 초기화 및 사이트 정리 코드를 추가해야 합니다. "깨지지 않은 코드는 건들지 말라"는 생각에를 그대로 두고
를 하나 더 작성하고, 안에 추가된 코드를 넣고, inner()
과 동일한 연산을 제공하기로 했습니다. > 인터페이스.
는 다음 다섯 가지 작업도 수행해야 합니다. inner()
outer()
必须生成一个generator;
在每一步的迭代中,outer()
要帮助inner()
返回迭代值;
在每一步的迭代中,outer()
要帮助inner()
接收外部发送的数据;
在每一步的迭代中,outer()
要处理inner()
接收和抛出所有异常;
在outer()
被close的时候,inner()
也要被正确地close掉。
根据上面的要求,在只有yield的世界里,outer()
可能是长这样的:
def outer1(): print("Before inner(), I do this.") i_gen = inner() input_val = None ret_val = i_gen.send(input_val) while True: try: input_val = yield ret_val ret_val = i_gen.send(input_val) except StopIteration: break except Exception as err: try: ret_val = i_gen.throw(err) except StopIteration: break print("After inner(), I do that.")
WTF,这段代码比inner()
本身还要长,而且还没处理close操作。
现在我们来试试外星科技:
def outer2(): print("Before inner(), I do this.") yield from inner() print("After inner(), I do that.")
除了完全符合上面的要求外,这四行代码打印出来的时候还能省点纸。
我们可以在outer1()
和outer2()
上分别测试 数据 以及 异常 的传递,不难发现这两个generator的行为基本上是一致的。既然如此, 外星科技当然在大多数情况下是首选。
对generator和coroutine的疑问
从以前接触到Python下的coroutine就觉得它怪怪的,我能看清它们的 行为模式,但是并不明白为什么要使用这种模式,generator和 coroutine具有一样的对外接口,是generator造就了coroutine呢,还 是coroutine造就了generator?最让我百思不得其解的是,Python下 的coroutine将“消息传递”和“调度”这两种操作绑在一个yield 上——即便有了yield from
,这个状况还是没变过——我看不出这样做 的必要性。如果一开始就从语法层面将这两种语义分开,并且为 generator和coroutine分别设计一套接口,coroutine的概念大概也会 容易理解一些。
更多Python 3中的yield from语法详解相关文章请关注PHP中文网!