제너레이터를 이용할 때 양방향을 사용할 수 있으면 좋을 것 같다.
import math
def wave(amplitude, steps):
step_size = 2 * math.pi / steps
for step in range(steps):
radians = step * step_size
fraction = math.sin(radians)
output = amplitude * fraction
yield output
def transmit(output):
if output is None:
print(f'출력: None')
else:
print(f'출력: {output:>5.1f}')
def run(it):
for output in it:
transmit(output)
run(wave(3.0, 8))
출력: 0.0 출력: 2.1 출력: 3.0 출력: 2.1 출력: 0.0 출력: -2.1 출력: -3.0 출력: -2.1
파이썬 제너레이터는 send 메서드를 지원한다.
이 메서드는 yield 식을 양방향 채널로 격상시켜준다.
send 메서드를 사용하면 입력을 제너레이터에 스트리밍하는 동시에 출력을 내보낼 수 이싿.
일반적으로 제너레이터를 이터레이션할 때 yield 식이 반환하는 값은 None이다.
def my_generator():
received = yield 1
print(f'받은 값 = {received}')
it = iter(my_generator())
output = next(it)
print(f'출력값 = {output}')
출력값 = 1
try:
next(it)
except StopIteration:
pass
받은 값 = None
하지만 for 루프나 next 내장 함수로 제너레이터를 이터레이션하지 않고 send 메서드를 호출하면, 제너레이터가 재개될 때 yield가 send에 전달된 파라미터 값을 반환한다.
it = iter(my_generator())
output = it.send(None)
print(f'출력값 = {output}')
try:
it.send('안녕!')
except StopIteration:
pass
출력값 = 1 받은 값 = 안녕!
def wave_modulating(steps):
step_size = 2 * math.pi / steps
amplitude = yield
for step in range(steps):
radians = step * step_size
fraction = math.sin(radians)
output = amplitude * fraction
amplitude = yield output
def run_modulating(it):
aplitudes = [
None, 7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10
]
for amplitude in aplitudes:
output = it.send(amplitude)
transmit(output)
run_modulating(wave_modulating(12))
출력: None 출력: 0.0 출력: 3.5 출력: 6.1 출력: 2.0 출력: 1.7 출력: 1.0 출력: 0.0 출력: -5.0 출력: -8.7 출력: -10.0 출력: -8.7 출력: -5.0
def complex_wave():
yield from wave(7.0, 3)
yield from wave(2.0, 4)
yield from wave(10.0, 5)
run(complex_wave())
출력: 0.0 출력: 6.1 출력: -6.1 출력: 0.0 출력: 2.0 출력: 0.0 출력: -2.0 출력: 0.0 출력: 9.5 출력: 5.9 출력: -5.9 출력: -9.5
def complex_wave_modulating():
yield from wave_modulating(3)
yield from wave_modulating(4)
yield from wave_modulating(5)
run_modulating(complex_wave_modulating())
출력: None 출력: 0.0 출력: 6.1 출력: -6.1 출력: None 출력: 0.0 출력: 2.0 출력: 0.0 출력: -10.0 출력: None 출력: 0.0 출력: 9.5 출력: 5.9
def wave_cascading(amplitude_it, steps):
step_size = 2 * math.pi / steps
for step in range(steps):
radians = step * step_size
fraction = math.sin(radians)
amplitude = next(amplitude_it)
output = amplitude * fraction
yield output
def complex_wave_cascading(amplitude_it):
yield from wave_cascading(amplitude_it, 3)
yield from wave_cascading(amplitude_it, 4)
yield from wave_cascading(amplitude_it, 5)
def run_cascading():
amplitudes = [7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10]
it = complex_wave_cascading(iter(amplitudes))
for amplitude in amplitudes:
output = next(it)
transmit(output)
run_cascading()
출력: 0.0 출력: 6.1 출력: -6.1 출력: 0.0 출력: 2.0 출력: 0.0 출력: -2.0 출력: 0.0 출력: 9.5 출력: 5.9 출력: -5.9 출력: -9.5
이 코드는 입력 제너레이터가 완전히 스레드 안전 하다고 가정한다는 단점이 있다.
하지만 제너레이터가 항상 스레드 안전하지는 않다.
따라서 스레드 경계를 넘나들면서 제너레이터를 사용해야 한다면 async 함수가 더 나은 해법일 수도 있다.