Python 제너레이터에서 "보내기" 함수의 목적은 무엇입니까?
Python generator 함수와 관련된 "send" 함수가 왜 존재하는지 예를 들어 주시겠습니까?항복 함수는 충분히 이해했습니다.하지만 송신기능이 혼란스럽습니다.이 방법에 대한 설명서는 다음과 같이 복잡합니다.
generator.send(value)
실행을 재개하고 생성기 함수로 값을 "전송"합니다.value 인수는 현재 수율식의 결과가 됩니다.send() 메서드는 제너레이터에 의해 산출된 다음 값을 반환하거나 제너레이터가 다른 값을 산출하지 않고 종료되면 StopIteration을 실행합니다.
그게 무슨 의미죠?함수에 대한 입력이 가치인 줄 알았는데?"The send() method returns the next value by generator"라는 문구도 항복 함수의 정확한 목적인 것 같습니다.즉, generator에 의해 산출된 다음 값을 반환합니다.
Send를 이용하는 발전기의 예를 들어 줄 수 없는 것을 얻을 수 있습니까?
방금 산출된 생성기에 값을 보내는 데 사용됩니다.다음으로 인위적인(유용하지 않은) 설명 예를 제시하겠습니다.
>>> def double_inputs():
... while True:
... x = yield
... yield x * 2
...
>>> gen = double_inputs()
>>> next(gen) # run up to the first yield
>>> gen.send(10) # goes into 'x' variable
20
>>> next(gen) # run up to the next yield
>>> gen.send(6) # goes into 'x' again
12
>>> next(gen) # run up to the next yield
>>> gen.send(94.3) # goes into 'x' again
188.5999999999999
만 할는 없어요.yield.
이 되는지에 의 Twisted가 좋은 사용 사례 중 입니다.@defer.inlineCallbacks기본적으로 다음과 같은 함수를 쓸 수 있습니다.
@defer.inlineCallbacks
def doStuff():
result = yield takesTwoSeconds()
nextResult = yield takesTenSeconds(result * 10)
defer.returnValue(nextResult / 10)
일이 일어나느냐 takesTwoSeconds()를 반환하다Deferred값은 나중에 계산될 것으로 예상되는 값입니다.뒤틀리다됩니다.doStuff() 이 '이렇게'는doStuff()모든 종류의 계산과 콜백 등을 실행할 수 있다는 점을 제외하면, 어느 정도 일반적인 절차 함수처럼 보일 수 있습니다.같은 방법도 .
def doStuff():
returnDeferred = defer.Deferred()
def gotNextResult(nextResult):
returnDeferred.callback(nextResult / 10)
def gotResult(result):
takesTenSeconds(result * 10).addCallback(gotNextResult)
takesTwoSeconds().addCallback(gotResult)
return returnDeferred
그것은 훨씬 더 복잡하고 다루기 힘들다.
이 기능은 코루틴을 쓰는 것입니다.
def coroutine():
for i in range(1, 10):
print("From generator {}".format((yield i)))
c = coroutine()
c.send(None)
try:
while True:
print("From user {}".format(c.send(1)))
except StopIteration: pass
인쇄하다
From generator 1
From user 2
From generator 1
From user 3
From generator 1
From user 4
...
컨트롤이 어떻게 앞뒤로 전달되는지 보여?그것들은 코루틴입니다.비동기 IO 등 모든 종류의 쿨한 작업에 사용할 수 있습니다.
이렇게 생각해 보세요. 발전기가 있으면 전송이 안 되고 일방통행입니다.
========== yield ========
Generator | ------------> | User |
========== ========
하지만 전송과 함께, 그것은 양방향 거리가 된다.
========== yield ========
Generator | ------------> | User |
========== <------------ ========
send
이를 통해 사용자는 제너레이터의 동작을 즉시 커스터마이즈하고 제너레이터가 사용자에게 응답할 수 있습니다.
이게 도움이 될 수도 있어요.송신 함수의 영향을 받지 않는 제너레이터입니다.인스턴스화 시 number 파라미터가 적용되며 송신에 영향을 받지 않습니다.
>>> def double_number(number):
... while True:
... number *=2
... yield number
...
>>> c = double_number(4)
>>> c.send(None)
8
>>> c.next()
16
>>> c.next()
32
>>> c.send(8)
64
>>> c.send(8)
128
>>> c.send(8)
256
다음은 send를 사용하여 동일한 유형의 함수를 수행하는 방법입니다. 따라서 반복할 때마다 숫자 값을 변경할 수 있습니다.
def double_number(number):
while True:
number *= 2
number = yield number
이 값은 다음과 같습니다.번호의 새로운 값을 전송하면 결과가 바뀝니다.
>>> def double_number(number):
... while True:
... number *= 2
... number = yield number
...
>>> c = double_number(4)
>>>
>>> c.send(None)
8
>>> c.send(5) #10
10
>>> c.send(1500) #3000
3000
>>> c.send(3) #6
6
다음과 같이 for 루프에 넣을 수도 있습니다.
for x in range(10):
n = c.send(n)
print n
자세한 내용은 이 훌륭한 튜토리얼을 참조하십시오.
send()합니다.
수율이 어떻게 다른지, 그리고 수율이 어떤 값을 보유하는지 이해하기 위해 먼저 python 코드 순서로 빠르게 새로 고칩니다.
Python은 왼쪽에서 오른쪽으로 식을 평가합니다.과제를 평가할 때 오른쪽이 왼쪽보다 먼저 평가된다는 점에 유의하십시오.
그래서 표현은a = b이치노
와 같이, 「 」는 다음과 같습니다.a[p('left')] = p('right')이치노
>>> def p(side):
... print(side)
... return 0
...
>>> a[p('left')] = p('right')
right
left
>>>
>>>
>>> [p('left'), p('right')]
left
right
[0, 0]
이 함수는 무엇을 하는 것입니까?양보, 함수의 실행을 중지하고 호출자에게 반환하며 일시정지하기 전에 중단했던 장소에서 실행을 재개합니다.
정확히 어디에서 집행이 중단되었습니까?이미 짐작하셨을 수도 있지만...실행이 항복식의 오른쪽과 왼쪽 사이에서 일시 중단됩니다.따라서 실행이 중지됩니다.=부호 및 오른쪽 값(일시정지 전, 발신자에게 반환되는 값이기도 함)은 왼쪽 값(실행 재개 후 할당되는 값)과는 다를 수 있습니다.
yield는 오른쪽과 왼쪽으로 각각2개의 값을 산출합니다.
항 the 、 the the the the the the the the the the the?를 경유하여.send()★★★★★★ 。
재개 후의 항복식의 값은 실행을 재개한 메서드에 따라 달라집니다. if
__next__()으로 for 을 사용합니다next()[없음]을 클릭합니다. 이외의 「」의 경우.send()이 사용되면 결과는 해당 메서드에 전달된 값이 됩니다.
및 발전기를 사례도 .send()
(발전기)send() 삭제:
- 실행 내부 상태 기억
- 우리가 어떤 단계에 있는지
- 우리 데이터의 현재 상태는 어떻습니까?
- 반환되는 값의 시퀀스
- 입력 시퀀스 수신
다음은 몇 가지 사용 사례입니다.
레시피를 따르려는 시도를 지켜봤다.
미리 정의된 입력 세트를 어떤 순서로 예상하는 레시피를 알려드리겠습니다.
다음과 같은 경우가 있습니다.
watched_attempt의 예- 입력 정보를 얻을 수 있도록 하다
- 각 입력 반환 정보와 함께 현재 냄비에 있는 것에 대한 정보
각 입력 체크에서 입력이 예상된 입력인지 여부(및 그렇지 않은 경우 실패)
def recipe(): pot = [] action = yield pot assert action == ("add", "water") pot.append(action[1]) action = yield pot assert action == ("add", "salt") pot.append(action[1]) action = yield pot assert action == ("boil", "water") action = yield pot assert action == ("add", "pasta") pot.append(action[1]) action = yield pot assert action == ("decant", "water") pot.remove("water") action = yield pot assert action == ("serve") pot = [] yield pot
" " " 를 .watched_attempt★★★★★★★★★★★★★★★★★★:
>>> watched_attempt = recipe()
>>> watched_attempt.next()
[]
의 콜.next()생성기 실행을 시작하기 위해 필요합니다.
반환된 값은 현재 냄비가 비어 있음을 나타냅니다.
이제 레시피가 기대하는 대로 몇 가지 작업을 수행합니다.
>>> watched_attempt.send(("add", "water"))
['water']
>>> watched_attempt.send(("add", "salt"))
['water', 'salt']
>>> watched_attempt.send(("boil", "water"))
['water', 'salt']
>>> watched_attempt.send(("add", "pasta"))
['water', 'salt', 'pasta']
>>> watched_attempt.send(("decant", "water"))
['salt', 'pasta']
>>> watched_attempt.send(("serve"))
[]
보다시피, 마침내 냄비가 비워졌다.
레시피를 따르지 않을 경우, 그것은 실패할 것이다(뭔가를 요리하려는 시도를 지켜본 결과 바람직한 결과를 얻을 수 있었다- 단지 우리가 지시를 받았을 때 충분한 주의를 기울이지 않았다는 것을 알게 되었을 뿐이다).
>>> watched_attempt = running.recipe()
>>> watched_attempt.next()
[]
>>> watched_attempt.send(("add", "water"))
['water']
>>> watched_attempt.send(("add", "pasta"))
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-21-facdf014fe8e> in <module>()
----> 1 watched_attempt.send(("add", "pasta"))
/home/javl/sandbox/stack/send/running.py in recipe()
29
30 action = yield pot
---> 31 assert action == ("add", "salt")
32 pot.append(action[1])
33
AssertionError:
주의:
- 예상되는 단계의 선형 시퀀스가 있다
- 스텝이 다를 수 있습니다(일부는 분리, 일부는 냄비에 추가).
- 복잡한 클래스나 유사한 스트럿을 사용할 필요 없이 기능/기능을 통해 이 모든 것을 할 수 있습니다.
실행 합계
제너레이터를 사용하여 제너레이터로 전송되는 총 실행 값을 추적할 수 있습니다.
숫자를 추가할 때마다 입력 개수 및 총합이 반환됩니다(이전 입력이 전송된 순간부터 유효).
from collections import namedtuple
RunningTotal = namedtuple("RunningTotal", ["n", "total"])
def runningtotals(n=0, total=0):
while True:
delta = yield RunningTotal(n, total)
if delta:
n += 1
total += delta
if __name__ == "__main__":
nums = [9, 8, None, 3, 4, 2, 1]
bookeeper = runningtotals()
print bookeeper.next()
for num in nums:
print num, bookeeper.send(num)
출력은 다음과 같습니다.
RunningTotal(n=0, total=0)
9 RunningTotal(n=1, total=9)
8 RunningTotal(n=2, total=17)
None RunningTotal(n=2, total=17)
3 RunningTotal(n=3, total=20)
4 RunningTotal(n=4, total=24)
2 RunningTotal(n=5, total=26)
1 RunningTotal(n=6, total=27)
sendmethod는 Coroutines를 구현합니다.
Coroutines를 접해본 적이 없는 경우 프로그램의 흐름을 바꾸기 때문에 쉽게 이해할 수 없습니다.자세한 내용은 좋은 튜토리얼을 참조하십시오.
"양식"이라는 단어는 두 가지 의미를 가지고 있습니다: 무언가를 생산하는 것(예를 들어, 옥수수를 생산하는 것)과 다른 누군가/물건을 계속하도록 멈추는 것(예를 들어, 보행자에게 양보하는 자동차)입니다.모두 의 Python에 됩니다.yield제너레이터 함수를 특별하게 하는 것은, 통상의 함수와는 달리, 값이 발신자에게 「일시」되는 것과 동시에, 제너레이터 함수를 종료하는 것이 아니라 일시정지할 수 있기 때문입니다.
제너레이터를 "왼쪽" 끝과 "오른쪽" 끝이 있는 양방향 파이프의 한쪽 끝이라고 가장 쉽게 생각할 수 있습니다. 이 파이프는 제너레이터 자체와 제너레이터 함수 본체 사이에 값이 전송되는 매체입니다.의 양 에는 두 조작이 . 즉, 파이프의 양 끝에는 두 가지 조작이 .push이 값을 하지 않습니다. 「」는 를 참조해 주세요.pull파이프의 다른 한쪽 끝이 값을 푸시할 때까지 차단하고 푸시된 값을 반환합니다.실행 시 파이프 양쪽에 있는 콘텍스트 간에 앞뒤로 바운스가 이루어집니다.각 측에서는 값이 송신될 때까지 실행되며, 그 시점에서 값이 정지하고 다른 쪽이 실행되며, 그 결과 값이 반환되기를 기다립니다.이 시점에서 다른 쪽이 정지하고 재개됩니다.즉, 파이프의 각 끝은 값을 받는 순간부터 값을 보내는 순간까지 계속됩니다.
파이프는 대칭이지만,답변에서 에 따라) 끝은 할 수 , 를 할 수 .yield키워드(오른쪽 끝은 제너레이터이며 제너레이터를 통해 액세스 가능)send양 에 대한 로서, 파이프 양 에 대한 단일 인터페이스로서yield ★★★★★★★★★★★★★★★★★」send역할을 : 둘 다 값을 . 을 사용법yield으로 으로 당기는 동안send그 반대입니다.입니다.x = yield y. 부시는yield ★★★★★★★★★★★★★★★★★」send인 푸시 그 명확해집니다. 즉, '푸시/풀 스텝'은 '푸시/는 '푸시/풀 스텝'입니다.
- 가정하다
g을 사용하다g.send파이프 오른쪽 끝을 통해 값을 왼쪽으로 밀어 넣습니다. - 의
g일시 중지하면 제너레이터 기능의 본체가 실행될 수 있습니다. - 에 의해 값
g.send으로 당기다yield파이프 왼쪽 끝에 수신했습니다. »x = yield y,x는 풀된 값에 할당됩니다. - 함수의 은 발생기 함수의 되며, 다음 행에는 발생기 함수가 포함됩니다.
yield달했습습니니다 yield값을 이 값은 다시 '파이프 왼쪽 끝'으로 .g.sendx = yield y,y파이프를 통해 오른쪽으로 밀립니다.- 제너레이터 기능의 본문 내에서 실행이 일시 중지되어 외부 스코프가 중단된 위치에서 계속됩니다.
g.send는 값을 재개하고 풀하여 사용자에게 반환합니다.g.send다음으로 호출됩니다.1번
순환적이긴 이. '시작이 있다'는 것입니다.g.send(None)은 next(g)입니다).None부터 끝까지send및 수 더 이상 하지 않을 종료됩니다.yield제너레이터 기능의 본문에서 도달해야 하는 문.
'보다 더 좋은 은 없다'는 것을 알 수 ?yield(또는 더 정확히 말하면 발전기) 그렇게 특별한가요?하찮은 것과는 달리return " " ",yield 있는 를 종료하지 할 수 제너레이터를 종료하고 「」를 이 편리합니다).return이치노의 경우yield문이 발견되면 제너레이터 함수는 일시 중지되고 다른 값이 전송되었을 때 중단되었던 위치에서 다시 작동합니다. ★★★★★★★★★★★★★★★★★.send외부로부터 제너레이터 함수의 내부와 통신하기 위한 인터페이스입니다.
한 1~제외하고 yield ★★★★★★★★★★★★★★★★★」send같은 면의 양면이다
화폐를 만들다 파이프:
right_end.push(None) # the first half of g.send; sending None is what starts a generatorright_end.pause()left_end.start()initial_value = left_end.pull()if initial_value is not None: raise TypeError("can't send non-None value to a just-started generator")left_end.do_stuff()left_end.push(y) # the first half of yieldleft_end.pause()right_end.resume()value1 = right_end.pull() # the second half of g.sendright_end.do_stuff()right_end.push(value2) # the first half of g.send (again, but with a different value)right_end.pause()left_end.resume()x = left_end.pull() # the second half of yieldgoto 6
한 입니다.x = yield y ★★★★★★★★★★★★★★★★★」value1 = g.send(value2)문장으로 left_end.push(y) ★★★★★★★★★★★★★★★★★」x = left_end.pull()및 ; »value1 = right_end.pull() ★★★★★★★★★★★★★★★★★」right_end.push(value2)에는 두 가지 특별한 경우가 있습니다.yield: " " " :x = yield ★★★★★★★★★★★★★★★★★」yield yx = yield None ★★★★★★★★★★★★★★★★★」_ = yield y # discarding value.
파이프를 통해 값이 전송되는 정확한 순서에 대한 자세한 내용은 아래를 참조하십시오.
다음은 위의 비교적 긴 콘크리트 모델입니다. 임의의 , 「」라고 하는 .g,next(g) 일치하다g.send(None)에 두고 어떻게 해야 에만 을 맞출 수 send의 진전에 대해서만 .send.
예를 들어
def f(y): # This is the "generator function" referenced above
while True:
x = yield y
y = x
g = f(1)
g.send(None) # yields 1
g.send(2) # yields 2
그럼 이제 , 의의 정의는요.f대략 다음과 같은 통상(비표준) 함수에 대해 제외합니다.
def f(y):
bidirectional_pipe = BidirectionalPipe()
left_end = bidirectional_pipe.left_end
right_end = bidirectional_pipe.right_end
def impl():
initial_value = left_end.pull()
if initial_value is not None:
raise TypeError(
"can't send non-None value to a just-started generator"
)
while True:
left_end.push(y)
x = left_end.pull()
y = x
def send(value):
right_end.push(value)
return right_end.pull()
right_end.send = send
# This isn't real Python; normally, returning exits the function. But
# pretend that it's possible to return a value from a function and then
# continue execution -- this is exactly the problem that generators were
# designed to solve!
return right_end
impl()
.f:
- 구현이 중첩된 함수로 이동되었습니다.
- .
left_end, 그 함수의 "접근"은 "접근"입니다.right_end-- by will -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --에 의해 되어 액세스 됩니다.right_end우리가 발전기 개체로 알고 있는 것입니다. - 함수에서 할 은 '네스트 함수'에서 '네스트 함수'를 입니다.
left_end.pull()None이치노 - 함수에서는, 「」라고 하는 이 표시됩니다.
x = yield y는 두되었습니다.left_end.push(y)★★★★★★★★★★★★★★★★★」x = left_end.pull(). - 이렇게 를 내렸습니다.
send의 기능을 .right_end두두 줄, 즉 2줄, 즉 2줄, 2줄로x = yield y이치노
이 복귀 에서는, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」gright_end 다음에 또 한 번.impl()에서 한 한 줄 위의 예에서는 실행 순서를 순서대로 실행하면 대략 다음과 같이 됩니다.
left_end = bidirectional_pipe.left_end
right_end = bidirectional_pipe.right_end
y = 1 # from g = f(1)
# None pushed by first half of g.send(None)
right_end.push(None)
# The above push blocks, so the outer scope halts and lets `f` run until
# *it* blocks
# Receive the pushed value, None
initial_value = left_end.pull()
if initial_value is not None: # ok, `g` sent None
raise TypeError(
"can't send non-None value to a just-started generator"
)
left_end.push(y)
# The above line blocks, so `f` pauses and g.send picks up where it left off
# y, aka 1, is pulled by right_end and returned by `g.send(None)`
right_end.pull()
# Rinse and repeat
# 2 pushed by first half of g.send(2)
right_end.push(2)
# Once again the above blocks, so g.send (the outer scope) halts and `f` resumes
# Receive the pushed value, 2
x = left_end.pull()
y = x # y == x == 2
left_end.push(y)
# The above line blocks, so `f` pauses and g.send(2) picks up where it left off
# y, aka 2, is pulled by right_end and returned to the outer scope
right_end.pull()
x = left_end.pull()
# blocks until the next call to g.send
이는 위의 16단계 의사 코드에 정확히 매핑됩니다.
때가 닫혔을 때)의 그 한 것에 만, 기본적인 가 어떻게 는, 「」( 「파이프가 때)에 하게 알 수 .send용됩니니다다
이러한 분리 규칙을 사용하여 다음 두 가지 특별한 경우를 살펴보겠습니다.
def f1(x):
while True:
x = yield x
def f2(): # No parameter
while True:
x = yield x
, 그들은 같은 한다.f, 이라고 하는 것은 어떻게 다른가 yield스테이트먼트는 다음과 같이 변환됩니다.
def f1(x):
# ... set up pipe
def impl():
# ... check that initial sent value is None
while True:
left_end.push(x)
x = left_end.pull()
# ... set up right_end
def f2():
# ... set up pipe
def impl():
# ... check that initial sent value is None
while True:
left_end.push(x)
x = left_end.pull()
# ... set up right_end
첫 번째로 값이 전달되었습니다.f1는 처음에 푸시(송신)되고 다음으로 풀(송신)된 모든 값이 바로 푸시(송신)됩니다. 번째, ★★★★★★★★★★★★★★★★★★.x pushUnboundLocalError인스톨 됩니다.
저도 헷갈렸어요.다음은 신호를 교대로 생성하고 수신하는 제너레이터를 설정할 때 사용한 예입니다(수율, 수용, 수용).
def echo_sound():
thing_to_say = '<Sound of wind on cliffs>'
while True:
thing_to_say = (yield thing_to_say)
thing_to_say = '...'.join([thing_to_say]+[thing_to_say[-6:]]*2)
yield None # This is the return value of send.
gen = echo_sound()
print 'You are lost in the wilderness, calling for help.'
print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Hello!'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)
print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Is anybody out there?'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)
print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Help!'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)
출력은 다음과 같습니다.
You are lost in the wilderness, calling for help.
------
You hear: "<Sound of wind on cliffs>"
You yell "Hello!"
------
You hear: "Hello!...Hello!...Hello!"
You yell "Is anybody out there?"
------
You hear: "Is anybody out there?...there?...there?"
You yell "Help!"
itr.send(None) 것은 '아까보다'와 같은 입니다.next(itr)발전기의 수율에 따라 주어진 값을 주는 거죠
다음은 이를 명확하게 보여주는 예시로, 보다 실용적으로 사용할 수 있는 방법을 제시합니다.
def iterator_towards(dest=100):
value = 0
while True:
n = yield value
if n is not None:
dest = n
if dest > value:
value += 1
elif dest < value:
value -= 1
else:
return
num = iterator_towards()
for i in num:
print(i)
if i == 5:
num.send(0)
인쇄:
0
1
2
3
4
5
3
2
1
0
드:의:i == 5 0 '아예'가 아닙니다None할 수 있습니다.dest 다음 반복을 0.
값 5.、 5 、 4 の 가가가 the the4 の the the the the 뒤에 값.는 것은 nature 이 있기 입니다..send(0) 거죠.4인쇄되지 않은 값입니다.
를 continue같은 값을 다시 산출할 수 있습니다.
def iterator_towards(dest=100):
value = 0
while True:
n = yield value
if n is not None:
dest = n
continue
if dest > value:
value += 1
elif dest < value:
value -= 1
else:
return
이것에 의해, 리스트를 반복할 수 있을 뿐만 아니라, 동적으로 새로운 행선지 값을 즉석에서 송신할 수도 있습니다.
언급URL : https://stackoverflow.com/questions/19302530/whats-the-purpose-of-send-function-on-python-generators
'source' 카테고리의 다른 글
| 마리아에 REGEXP_REPLACE 함수가 없습니다.DB (0) | 2022.11.22 |
|---|---|
| PHP 날짜 현재 날짜로 5년 추가 (0) | 2022.11.22 |
| 치명적인 오류: mysqld를 루트로 실행하는 방법에 대해서는 매뉴얼의 "보안" 섹션을 참조하십시오. (0) | 2022.11.22 |
| python dict를 문자열로 변환한 후 되돌립니다. (0) | 2022.11.22 |
| 요청이 PHP를 사용한 AJAX 요청인지 확인하는 방법 (0) | 2022.11.13 |