{ "cells": [ { "cell_type": "markdown", "id": "tribal-chrome", "metadata": {}, "source": [ "# Iterators and Generators (21/4 - 2021)" ] }, { "cell_type": "markdown", "id": "faced-cornell", "metadata": {}, "source": [ "## Exercise\n", "\n", "Create an iterable class ``arange(start, end, step)`` to return values start, start + step, start + 2 * step, ..." ] }, { "cell_type": "code", "execution_count": null, "id": "direct-substitute", "metadata": { "scrolled": true }, "outputs": [], "source": [ "L = [1, 2, 3]\n", "for x in L: # L is an 'iterable'\n", " print(x)" ] }, { "cell_type": "code", "execution_count": null, "id": "unlikely-mailman", "metadata": {}, "outputs": [], "source": [ "it = iter(L) # it is an 'iterator'\n", "while True:\n", " x = next(it)\n", " print(x)" ] }, { "cell_type": "code", "execution_count": null, "id": "honey-alloy", "metadata": {}, "outputs": [], "source": [ "it = L.__iter__()\n", "while True:\n", " x = it.__next__()\n", " print(x)" ] }, { "cell_type": "code", "execution_count": null, "id": "republican-honey", "metadata": {}, "outputs": [], "source": [ "class arange:\n", " def __init__(self, start, end, step=1):\n", " self.start = start\n", " self.end = end\n", " self.step = step\n", " def __iter__(self):\n", " return arange_iterator(self)\n", " \n", " def __repr__(self):\n", " return f'arange({self.start}, {self.end}, {self.step})'\n", " \n", "class arange_iterator:\n", " def __init__(self, the_arange):\n", " self.the_arange = the_arange\n", " self.current = self.the_arange.start\n", " \n", " def __next__(self):\n", " value = self.current\n", " if value >= self.the_arange.end:\n", " raise StopIteration\n", " self.current += self.the_arange.step\n", " return value" ] }, { "cell_type": "code", "execution_count": null, "id": "forward-waterproof", "metadata": {}, "outputs": [], "source": [ "list(arange(1, 2, 0.25))" ] }, { "cell_type": "code", "execution_count": null, "id": "perceived-conversation", "metadata": {}, "outputs": [], "source": [ "# also allows limits to be infinity\n", "\n", "for i, x in enumerate(arange(10, float('inf'))):\n", " print(i, x)\n", " if i > 10:\n", " break" ] }, { "cell_type": "code", "execution_count": null, "id": "continued-gentleman", "metadata": {}, "outputs": [], "source": [ "class arange:\n", " def __init__(self, arg0, *args):\n", " if len(args) == 0:\n", " self.start, self.end, self.step = 0, arg0, 1\n", " elif len(args) == 1:\n", " self.start, self.end, self.step = arg0, *args, 1\n", " elif len(args) == 2:\n", " self.start, self.end, self.step = arg0, *args\n", " else:\n", " raise 'arange expected at most 3 arugments, got ' + str(1 + len(args))\n", " def __iter__(self):\n", " return arange_iterator(self)\n", " \n", " def __repr__(self):\n", " return f'arange({self.start}, {self.end}, {self.step})'" ] }, { "cell_type": "code", "execution_count": null, "id": "spare-lightweight", "metadata": {}, "outputs": [], "source": [ "list(arange(1, 5, 0.2))" ] }, { "cell_type": "code", "execution_count": null, "id": "corrected-defeat", "metadata": {}, "outputs": [], "source": [ "arange(2)" ] }, { "cell_type": "markdown", "id": "guided-opening", "metadata": {}, "source": [ "## Exercise\n", "\n", "Generator epxression to create powers of two." ] }, { "cell_type": "code", "execution_count": null, "id": "imposed-relief", "metadata": {}, "outputs": [], "source": [ "L = [1, 2, 3]\n", "print(sum(L)) # lists is iterable\n", "print(sum(range(1, 5))) # ranges are iterable\n", "P = [2 ** x for x in range(10)]\n", "print(sum(P))\n", "print(sum([2 ** x for x in range(10)]))\n", "print(sum(2 ** x for x in range(10))) # generator expression, shorthand for below\n", "print(sum( (2 ** x for x in range(10)) )) # generator expression\n", "G = (2 ** x for x in range(10)) # answer to exercise \n", "print(G)\n", "print(sum(G))\n", "print(sum(G)) # G has been exhausted above" ] }, { "cell_type": "code", "execution_count": null, "id": "armed-diabetes", "metadata": {}, "outputs": [], "source": [ "G = ((x, 2 ** x) for x in range(10))\n", "\n", "g = iter(G)\n", "while True:\n", " x = next(g)\n", " print(x)" ] }, { "cell_type": "code", "execution_count": null, "id": "ceramic-answer", "metadata": {}, "outputs": [], "source": [ "G = ((x, 2 ** x) for x in range(10))\n", "print(G)\n", "print(iter(G)) # iter just returns it self, \n", " # ie a generator expression is both iterable and an iterator" ] }, { "cell_type": "code", "execution_count": null, "id": "environmental-nature", "metadata": {}, "outputs": [], "source": [ "# combining generator expression\n", "\n", "g1 = (x ** 2 for x in range(5))\n", "g2 = (x + 3 for x in g1)\n", "\n", "print(g2)\n", "print(list(g2))\n", "print(list(g2)) # generator expressions are use-once" ] }, { "cell_type": "markdown", "id": "stupid-encounter", "metadata": {}, "source": [ "## Exercise\n", "\n", "Create generator ``g(n)`` to generate all pairs (x, y) where x and y are from range(n) and x + y is not divisble by 3." ] }, { "cell_type": "code", "execution_count": null, "id": "immune-preservation", "metadata": {}, "outputs": [], "source": [ "def g(n):\n", " for x in range(n):\n", " for y in range(n):\n", " z = x + y\n", " if z % 3 != 0:\n", " yield (x, y) # presence of yield makes this a GENERATOR instead of a FUNCTION\n", "\n", "#print(g(3))\n", "#o = g(3)\n", "#it = iter(o)\n", "#while True:\n", "# z = next(it)\n", "# print(z)\n", " \n", "for z in g(3):\n", " print(z)" ] }, { "cell_type": "code", "execution_count": null, "id": "tested-image", "metadata": {}, "outputs": [], "source": [ "def g():\n", " yield 1\n", " yield 2\n", " yield from [-1, -2, -3] # yield from iterable\n", " yield from (5 ** x for x in range(3))\n", " return 42 # return raises a StopIteration exception\n", " yield 3\n", " yield 4\n", " \n", "print(list(g()))\n", "\n", "#it = iter(g())\n", "it = g() # g is both iterable and iterator\n", "print(next(it))\n", "print(next(it))\n", "print(next(it))\n", "print(next(it))\n", "print(next(it))\n", "print(next(it))\n", "print(next(it))\n", "print(next(it))\n", "print(next(it))" ] }, { "cell_type": "code", "execution_count": null, "id": "dental-anger", "metadata": {}, "outputs": [], "source": [ "def arange(start, end, step):\n", " value = start\n", " while value < end:\n", " yield value\n", " value += step\n", " \n", "list(arange(12, 13.5, 0.1))" ] } ], "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.9.4" } }, "nbformat": 4, "nbformat_minor": 5 }