{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## 34. send로 제너레이터에 데이터를 주입하지 말라" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "제너레이터를 이용할 때 양방향을 사용할 수 있으면 좋을 것 같다." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import math" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def wave(amplitude, steps):\n", " step_size = 2 * math.pi / steps\n", " for step in range(steps):\n", " radians = step * step_size\n", " fraction = math.sin(radians)\n", " output = amplitude * fraction\n", " yield output" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def transmit(output):\n", " if output is None:\n", " print(f'출력: None')\n", " else:\n", " print(f'출력: {output:>5.1f}')" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def run(it):\n", " for output in it:\n", " transmit(output)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "출력: 0.0\n", "출력: 2.1\n", "출력: 3.0\n", "출력: 2.1\n", "출력: 0.0\n", "출력: -2.1\n", "출력: -3.0\n", "출력: -2.1\n" ] } ], "source": [ "run(wave(3.0, 8))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "파이썬 제너레이터는 send 메서드를 지원한다.\n", "\n", "이 메서드는 yield 식을 양방향 채널로 격상시켜준다.\n", "\n", "send 메서드를 사용하면 입력을 제너레이터에 스트리밍하는 동시에 출력을 내보낼 수 이싿.\n", "\n", "일반적으로 제너레이터를 이터레이션할 때 yield 식이 반환하는 값은 None이다." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def my_generator():\n", " received = yield 1\n", " print(f'받은 값 = {received}')" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "출력값 = 1\n" ] } ], "source": [ "it = iter(my_generator())\n", "output = next(it)\n", "print(f'출력값 = {output}')" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "받은 값 = None\n" ] } ], "source": [ "try:\n", " next(it)\n", "except StopIteration:\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "하지만 for 루프나 next 내장 함수로 제너레이터를 이터레이션하지 않고 send 메서드를 호출하면, 제너레이터가 재개될 때 yield가 send에 전달된 파라미터 값을 반환한다." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "출력값 = 1\n", "받은 값 = 안녕!\n" ] } ], "source": [ "it = iter(my_generator())\n", "output = it.send(None)\n", "print(f'출력값 = {output}')\n", "\n", "try:\n", " it.send('안녕!')\n", "except StopIteration:\n", " pass" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "def wave_modulating(steps):\n", " step_size = 2 * math.pi / steps\n", " amplitude = yield\n", " for step in range(steps):\n", " radians = step * step_size\n", " fraction = math.sin(radians)\n", " output = amplitude * fraction\n", " amplitude = yield output" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "def run_modulating(it):\n", " aplitudes = [\n", " None, 7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10\n", " ]\n", " for amplitude in aplitudes:\n", " output = it.send(amplitude)\n", " transmit(output)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "출력: None\n", "출력: 0.0\n", "출력: 3.5\n", "출력: 6.1\n", "출력: 2.0\n", "출력: 1.7\n", "출력: 1.0\n", "출력: 0.0\n", "출력: -5.0\n", "출력: -8.7\n", "출력: -10.0\n", "출력: -8.7\n", "출력: -5.0\n" ] } ], "source": [ "run_modulating(wave_modulating(12))" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "def complex_wave():\n", " yield from wave(7.0, 3)\n", " yield from wave(2.0, 4)\n", " yield from wave(10.0, 5)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "출력: 0.0\n", "출력: 6.1\n", "출력: -6.1\n", "출력: 0.0\n", "출력: 2.0\n", "출력: 0.0\n", "출력: -2.0\n", "출력: 0.0\n", "출력: 9.5\n", "출력: 5.9\n", "출력: -5.9\n", "출력: -9.5\n" ] } ], "source": [ "run(complex_wave())" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "def complex_wave_modulating():\n", " yield from wave_modulating(3)\n", " yield from wave_modulating(4)\n", " yield from wave_modulating(5)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "출력: None\n", "출력: 0.0\n", "출력: 6.1\n", "출력: -6.1\n", "출력: None\n", "출력: 0.0\n", "출력: 2.0\n", "출력: 0.0\n", "출력: -10.0\n", "출력: None\n", "출력: 0.0\n", "출력: 9.5\n", "출력: 5.9\n" ] } ], "source": [ "run_modulating(complex_wave_modulating())" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "def wave_cascading(amplitude_it, steps):\n", " step_size = 2 * math.pi / steps\n", " for step in range(steps):\n", " radians = step * step_size\n", " fraction = math.sin(radians)\n", " amplitude = next(amplitude_it)\n", " output = amplitude * fraction\n", " yield output" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "def complex_wave_cascading(amplitude_it):\n", " yield from wave_cascading(amplitude_it, 3)\n", " yield from wave_cascading(amplitude_it, 4)\n", " yield from wave_cascading(amplitude_it, 5)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "def run_cascading():\n", " amplitudes = [7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10]\n", " it = complex_wave_cascading(iter(amplitudes))\n", " for amplitude in amplitudes:\n", " output = next(it)\n", " transmit(output)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "출력: 0.0\n", "출력: 6.1\n", "출력: -6.1\n", "출력: 0.0\n", "출력: 2.0\n", "출력: 0.0\n", "출력: -2.0\n", "출력: 0.0\n", "출력: 9.5\n", "출력: 5.9\n", "출력: -5.9\n", "출력: -9.5\n" ] } ], "source": [ "run_cascading()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이 코드는 입력 제너레이터가 완전히 스레드 안전 하다고 가정한다는 단점이 있다.\n", "\n", "하지만 제너레이터가 항상 스레드 안전하지는 않다. \n", "\n", "따라서 스레드 경계를 넘나들면서 제너레이터를 사용해야 한다면 async 함수가 더 나은 해법일 수도 있다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 기억해야 할 내용\n", "- send 메서드를 사용해 데이터를 제너레이터에 주입할 수 있다. 제너레이터는 send로 주입된 값을 yield 식이 반환하는 값을 통해 받으며, 이 값을 변수에 저장해 활용할 수 있다.\n", "- send와 yield form 식을 함께 사용하면 제너레이터의 출력에 None이 불쑥불쑥 나타나는 의외의 결과를 얻을 수도 있다.\n", "- 합성할 제너레이터들의 입력을 이터레이터를 전달하는 방식이 send를 사용하는 방식보다 더 낫다. send는 가급적 사용하지 말라." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.2" } }, "nbformat": 4, "nbformat_minor": 4 }