{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Matematikai Algoritmusok és Felfedezések II.\n", "\n", "## 8. Dekorátorok\n", "\n", "### 2021 November 4." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Vajon mi történik, ha ezt lefuttatjuk?" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "30\n", "300\n" ] }, { "ename": "RecursionError", "evalue": "maximum recursion depth exceeded in comparison", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mRecursionError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtest\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m30\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 8\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtest\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m300\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtest\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m3000\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m\u001b[0m in \u001b[0;36mtest\u001b[1;34m(n)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mtest\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mn\u001b[0m\u001b[1;33m>\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mtest\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m+\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mn\u001b[0m\u001b[1;33m==\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "... last 1 frames repeated, from the frame below ...\n", "\u001b[1;32m\u001b[0m in \u001b[0;36mtest\u001b[1;34m(n)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mtest\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mn\u001b[0m\u001b[1;33m>\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mtest\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m+\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mn\u001b[0m\u001b[1;33m==\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mRecursionError\u001b[0m: maximum recursion depth exceeded in comparison" ] } ], "source": [ "def test(n):\n", " if n>1:\n", " return test(n-1)+1\n", " if n==1:\n", " return 1\n", "\n", "print(test(30))\n", "print(test(300))\n", "print(test(3000))\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "import sys\n", "sys.setrecursionlimit(3500)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3000\n" ] } ], "source": [ "print(test(3000))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Milyen gyorsan fut le a következő kód?\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "55\n", "89\n", "39088169\n", "Wall time: 30.2 s\n" ] } ], "source": [ "%%time\n", "def fibo(n):\n", " if n>2:\n", " return fibo(n-1)+fibo(n-2)\n", " if n==1 or n==2: \n", " return 1\n", "\n", "print(fibo(10))\n", "print(fibo(11))\n", "print(fibo(38))\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Fibo javítás:\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "my_cache={}\n", "\n", "def fibo(n):\n", " if n in my_cache:\n", " #print(\"reading cache\")\n", " return my_cache[n]\n", " if n>2:\n", " ans=fibo(n-1)+fibo(n-2)\n", " \n", " if n==1 or n==2:\n", " ans=1\n", " my_cache[n]=ans\n", " return ans\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Wall time: 0 ns\n" ] }, { "data": { "text/plain": [ "39088169" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%time\n", "fibo(38)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Még mindig nem az igazi. Miért nem menti el a python magától az eredményeket?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Egy függvény kimenetele feltétlen csak a bemenetektől függ! Így nem menthetők el az eredmények.\n", "Viszont megkérhetjük a pythont, hogy mentsen." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "from functools import lru_cache" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "\n", "@lru_cache(10000)\n", "def fibo(n):\n", " if n>2:\n", " return fibo(n-1)+fibo(n-2)\n", " if n==1 or n==2:\n", " return 1" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Wall time: 0 ns\n" ] }, { "data": { "text/plain": [ "43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%time\n", "fibo(1000)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "fibo_called=0\n", "\n", "@lru_cache(10000)\n", "def fibo(n):\n", " global fibo_called\n", " fibo_called=fibo_called+1\n", " if n>2:\n", " return fibo(n-1)+fibo(n-2)\n", " if n==1 or n==2:\n", " return 1" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fibo(10)\n", "fibo_called" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "# Cache\n", "\n", "Mentsük el azokat az eredményeket, melyek csak a bemenettől függenek. A gond, hogy ezekből túl sok van, ezért néha törölni kell.\n", "\n", "Különböző stratégiák léteznek arra, hogy mit dobjunk ki, ha már túl sokat mentettünk el:\n", "\n", "| név | törölni |\n", "|-------|-------|\n", "| First-In/First-Out (FIFO) | A legrégebben elmentett elemet |\n", "| Last-In/First-Out (LIFO) | A legfrissebben elmentett elemet |\n", "| Least Recently Used (LRU) | A legrégebben használt elmentett elemet |\n", "| Most Recently Used (MRU) | A legfrissebben használt elmentett elemet |\n", "| Least Frequently Used (LFU) | A legkevésbé gyakran használt elemet |" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "És természetesen ezek mindenféle kombinációja is hasznos lehet. Például idő és tárhely közös figyelembevételével való törlés. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Python függvények\n", "\n", "Python a függvények \"first class citizen\"-ek. Ez azt jelenti, hogy ugyanolyan objektumok, mint bármi más. " ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100\n" ] }, { "data": { "text/plain": [ "121" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def negyzet(n):\n", " return(n**2)\n", "\n", "print(negyzet(10))\n", "ez_is_negyzet=negyzet\n", "ez_is_negyzet(11) # ez is meghívató függvényként" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, 9, 16]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def map_to_list(f,lista): # át lehet adni egy függvényt\n", " ans=[]\n", " for i in lista:\n", " ans.append(f(i))\n", " return ans\n", "\n", "map_to_list(negyzet,[1,2,3,4])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Dekorátorok\n", "\n", "Egy függvény definíciója (a `def` utasítás) két dolgot csinál: létrehoz egy függvény objektumot és azt eltárolja olyan néven, amit megadtunk. A dekorátorok lehetővé teszik, hogy valamit „beszúrjunk” eközé a két lépés közé: létrejön a függvény objektum, meghívódik a dekorátor és megkapja paraméterként az éppen létrejött függvény objektumot, majd a dekorátor visszatérési értéke eltárolódik olyan néven, amit a függvény definíciójánál megadtunk." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "< Függvényhívás előtt\n", "A paraméterek: Run 120\n", "< Függvényhívás után\n" ] } ], "source": [ "def first_decorator(func):\n", " def inner(x, y):\n", " print(\"< Függvényhívás előtt\")\n", " func(x, y)\n", " print(\"< Függvényhívás után\")\n", "\n", " return inner\n", "\n", "\n", "def foo(x, y):\n", " \n", " print(\"A paraméterek: \", x, y)\n", "\n", "\n", "\n", "method = first_decorator(foo)\n", "method(\"Run\", 120)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "< Függvényhívás előtt\n", "A paraméterek: First run 100\n", "< Függvényhívás után\n", "< Függvényhívás előtt\n", "A paraméterek: Second run 120\n", "< Függvényhívás után\n" ] } ], "source": [ "def first_decorator(func):\n", " def inner(x, y):\n", " print(\"< Függvényhívás előtt\")\n", " func(x, y)\n", " print(\"< Függvényhívás után\")\n", "\n", " return inner\n", "\n", "@first_decorator\n", "def foo(x, y):\n", " print(\"A paraméterek: \", x, y)\n", "\n", " \n", "def foo2(x, y):\n", " print(\"A paraméterek: \", x, y)\n", "\n", "\n", "# Dekoralt funkcio meghivasa\n", "foo(\"First run\", 100)\n", "\n", "method = first_decorator(foo2)\n", "method(\"Second run\", 120)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "A dekorátorokat kukac karakterrel kell bevezetni:\n", "```python\n", "@callable_used_as_decorator\n", "def new_function(arguments):\n", " #... function body\n", "```\n", "Ahogyan fentebb leírtuk, ez **nagyjából** annak felel meg, mintha azt írtuk volna, hogy:\n", "```python\n", "def _temporary_function_object(arguments):\n", " #... function body\n", "new_function = callable_used_as_decorator(_temporary_function_object)\n", "```\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Példaképpen ez a (gyakorlatban nem túl hasznos) függvény meghívja a megkapott függvény objektumot, majd módosítás nélkül visszakapja azt:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "def run_immediately(func):\n", " func()\n", " return func" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Üdvözöllek, dicső lovag!\n", "spam, spam, spam\n", "Üdvözöllek, dicső lovag!\n", "Üdvözöllek, dicső lovag!\n" ] } ], "source": [ "@run_immediately\n", "def greet():\n", " print(\"Üdvözöllek, dicső lovag!\")\n", " \n", "print(\"spam, spam, spam\")\n", "greet()\n", "greet()" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true, "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Felhasználás 1\n", "Általában azonban olyan dolgokat akarunk dekorátorként használni, amelyek valahogy módosítják az éppen definiált függvényt. " ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "def cached(func):\n", " cache = {}\n", " def wrapper(arg):\n", " try:\n", " return cache[arg]\n", " except KeyError:\n", " result = func(arg)\n", " cache[arg] = result\n", " return result\n", " return wrapper" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ".wrapper at 0x000001CE556F9E58>\n", "x értéke? 1\n", "y értéke? 34\n", "['1', '34', '1']\n" ] } ], "source": [ "@cached\n", "def ask_for_value(name):\n", "\n", " return input(name+\" értéke? \")\n", "\n", "print(ask_for_value)\n", "\n", "results = []\n", "results.append(ask_for_value(\"x\"))\n", "results.append(ask_for_value(\"y\"))\n", "results.append(ask_for_value(\"x\"))\n", "print(results) " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Felhasználás 2\n", "\n", "Vegyük észre, hogy a függvény metaadatai (például neve) nem stimmelnek.\n", "\n", "Ennek az esztétikai problémának a korrigálására lehet importálni a `functools.wraps` függvényt, ami helyreteszi a metaadatokat:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "import functools\n", "\n", "def cached(func):\n", " cache = {}\n", " @functools.wraps(func)\n", " def wrapper(arg):\n", " try:\n", " return cache[arg]\n", " except KeyError:\n", " result = cache[arg] = func(arg)\n", " return result\n", " return wrapper" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "scrolled": false, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Fontos metaadatok:\n", "Név: ask_for_value\n", "Dokumentáció: Így szokás dokumentációt írni Pythonban.\n" ] } ], "source": [ "@cached\n", "def ask_for_value(name):\n", " '''Így szokás dokumentációt írni Pythonban.'''\n", " return input(name+\" értéke? \")\n", " \n", "print(ask_for_value)\n", "print(\"Fontos metaadatok:\")\n", "print(\"Név:\", ask_for_value.__name__)\n", "print(\"Dokumentáció:\", ask_for_value.__doc__)\n", "\n", "#futtatás kihagyva, ugyanúgy működne, mint előbb" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ez a példa illusztrálja, hogy a dekorátor kijelölésekor lehet adattag-elérést (pont operátor) és függvényhívást alkalmazni. \n", "\n", "Pontosabban fogalmazva `functools.wraps` nem egy dekorátor, hanem egy _dekorátor factory_: paraméterül kap egy függvényt (ahonnan veszi a metaadatok értékeit) és a visszatérési értékét fogjuk dekorátorként használni." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Mi is tudunk ilyen dekorátor factory-t írni:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Osztás eredménye: 1.0\n", "Osztás eredménye: -2.0\n", "Osztás eredménye: 0.125\n" ] }, { "data": { "text/plain": [ "[1.0, -2.0, 0.125]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from functools import wraps\n", "\n", "def logged(file, msg):\n", " def decorator(func):\n", " @wraps(func)\n", " def wrapper(*args, **kw):\n", " result = func(*args, **kw)\n", " file.write(msg + str(result) + \"\\n\")\n", " return result\n", " return wrapper\n", " return decorator\n", "\n", "import sys\n", "\n", "@logged(sys.stderr, \"Osztás eredménye: \")\n", "def divide(x, y):\n", " return x/y\n", "\n", "[divide(2,2), divide(16,-8), divide(1,8)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Itt sys.stderr a sztenderd hiba kimenet, amit békés rózsaszín háttérrel jelenít meg a Jupyter rendszer." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Felhasználás 3\n", "\n", "Dekorátorokat nem csak függvényekre, hanem osztályokra is lehet alkalmazni. Például a rendezési operátorok definícióját megcsinálja nekünk a `functools.total_ordering` dekorátor (csak az egyenlőséget és egy egyenlőtlenséget kell nekünk definiálnunk):" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True False True True False\n" ] } ], "source": [ "import functools\n", "\n", "@functools.total_ordering\n", "class Results:\n", " def __init__(self, win, loss):\n", " self.win = win\n", " self.loss = loss\n", " def adventage(self):\n", " return self.win-self.loss\n", " def __eq__(self, oth):\n", " \"\"\" operator==() \"\"\"\n", " return self.win == oth.win and self.loss == oth.loss\n", " def __lt__(self, oth):\n", " \"\"\" operator<() \"\"\"\n", " return (self.adventage(), self.win) < (oth.adventage(), oth.win)\n", "\n", "x = Results(6,3)\n", "y = Results(4,2)\n", "z = Results(4,1)\n", "w = Results(3,0)\n", "print(x>=y, x<=z, x!=w, w\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mr\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m10\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mAttributeError\u001b[0m: can't set attribute" ] } ], "source": [ "p.r = 10" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "... de definiálhatóak hozzájuk setterek is:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "8.0 6.0\n" ] } ], "source": [ "import math\n", "class Point:\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", " @property\n", " def angle(self):\n", " return math.atan2(self.x, self.y)\n", " @angle.setter\n", " def angle(self, value):\n", " r = self.r\n", " self.x = math.cos(value)*r\n", " self.y = math.sin(value)*r\n", " @property\n", " def r(self):\n", " return math.sqrt(self.x**2 + self.y**2)\n", " @r.setter\n", " def r(self, value):\n", " angle = self.angle\n", " self.x = math.cos(angle)*value\n", " self.y = math.sin(angle)*value\n", "\n", "p = Point(3,4)\n", "p.r = 10\n", "print(p.x, p.y)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Ahogyan látható, bármilyen számításokat elrejthetünk a property mögött, ennek persze az az ára, hogy a Python rendszer nem tudja és nem akarja ellenőrizni azt, hogy a property valóban kulturált adattagként viselkedik-e (például ha beleírunk egy értéket, akkor utána ugyanaz az érték lesz-e kiolvasható)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mire jó ez az egész?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "A propertyk létezésének nagy előnye, hogy nekik köszönhetően Pythonban egy osztály „publikus” interfészében nyugodtan lehetnek publikus adattagok.\n", "\n", "Ha egy adattaghoz később extra funkcionalitást akarunk csatolni (például egy beállítás-fájlból akarjuk kiolvasni vagy ellenőrizni akarjuk, hogy csak megfelelő értéket lehessen beleírni stb.), akkor bármikor lecserélhetjük egy property-re. (Az adattagok többségénél viszont ez sohasem fog bekövetkezni és azoknál élvezhetjük, hogy nem hígítják fel getter-setter metódusok a kódunkat.)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Felhasználás 5\n", "\n", "Típus ellenőrzés" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```\n", "def myMethod(ID, name):\n", " if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')):\n", " raise BlaBlaException() ...\n", "\n", " \n", "@accepts(uint, utf8string)\n", "def myMethod(ID, name):\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Fehasználás 6. Függvény regisztrálás, jogosultságok\n", "\n", "Discord bot\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```\n", "@client.event\n", "async def on_ready():\n", " guild = discord.utils.get(client.guilds, name=GUILD)\n", " print(\n", " f'{client.user} is connected to the following guild:\\n'\n", " f'{guild.name}(id: {guild.id})'\n", " )\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "```\n", "bot = commands.Bot(command_prefix='!')\n", "\n", "@bot.command(name='99')\n", "async def nine_nine(ctx):\n", " brooklyn_99_quotes = [\n", " 'I\\'m the human form of the 💯 emoji.',\n", " 'Bingpot!',\n", " (\n", " 'Cool. Cool cool cool cool cool cool cool, '\n", " 'no doubt no doubt no doubt no doubt.'\n", " ),\n", " ]\n", "\n", " response = random.choice(brooklyn_99_quotes)\n", " await ctx.send(response)```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "```\n", "@bot.command(name='roll_dice', help='Simulates rolling dice.')\n", "async def roll(ctx, number_of_dice, number_of_sides):\n", " dice = [\n", " str(random.choice(range(1, number_of_sides + 1)))\n", " for _ in range(number_of_dice)\n", " ]\n", " await ctx.send(', '.join(dice))```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "```\n", "@bot.command(name='create-channel')\n", "@commands.has_role('admin')\n", "async def create_channel(ctx, channel_name='real-python'):\n", " guild = ctx.guild\n", " existing_channel = discord.utils.get(guild.channels, name=channel_name)\n", " if not existing_channel:\n", " print(f'Creating a new channel: {channel_name}')\n", " await guild.create_text_channel(channel_name)\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Felhasználás 7. logolás" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```\n", "import functools\n", "\n", "def log(logger, level='info'):\n", " def log_decorator(fn):\n", " @functools.wraps(fn)\n", " def wrapper(*a, **kwa):\n", " getattr(logger, level)(fn.__name__)\n", " return fn(*a, **kwa)\n", " return wrapper\n", " return log_decorator\n", "\n", "# later that day ...\n", "@log(logging.getLogger('main'), level='warning')\n", "def potentially_dangerous_function(times):\n", " for _ in xrange(times): rockets.get_rocket(NUCLEAR=True).fire()\n", " ```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Felhasználás 8. Mérések\n", "\n", "Mennyi ideig fut egy függvény? Mennyi memóriát használunk? Stb..." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "from functools import wraps\n", "import tracemalloc\n", "from time import perf_counter \n", "\n", "\n", "def measure_performance(func):\n", " '''Measure performance of a function'''\n", "\n", " @wraps(func)\n", " def wrapper(*args, **kwargs):\n", " tracemalloc.start()\n", " start_time = perf_counter()\n", " func(*args, **kwargs)\n", " current, peak = tracemalloc.get_traced_memory()\n", " finish_time = perf_counter()\n", " print(f'Function: {func.__name__}')\n", " print(f'Method: {func.__doc__}')\n", " print(f'Memory usage:\\t\\t {current / 10**6:.6f} MB \\n'\n", " f'Peak memory usage:\\t {peak / 10**6:.6f} MB ')\n", " print(f'Time elapsed is seconds: {finish_time - start_time:.6f}')\n", " print(f'{\"-\"*40}')\n", " tracemalloc.stop()\n", " return wrapper" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "@measure_performance\n", "def make_list1():\n", " '''Range'''\n", "\n", " my_list = list(range(100000))\n", "\n", "\n", "@measure_performance\n", "def make_list2():\n", " '''List comprehension'''\n", "\n", " my_list = [l for l in range(100000)]\n", " \n", "@measure_performance\n", "def make_list3():\n", " '''Append'''\n", " \n", " my_list = []\n", " for item in range(100000):\n", " my_list.append(item)\n", "\n", "\n", "@measure_performance\n", "def make_list4():\n", " '''Concatenation'''\n", "\n", " my_list = []\n", " for item in range(100000):\n", " my_list = my_list + [item]" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Function: make_list1\n", "Method: Range\n", "Memory usage:\t\t 0.000904 MB \n", "Peak memory usage:\t 3.693756 MB \n", "Time elapsed is seconds: 0.081127\n", "----------------------------------------\n", "None\n", "Function: make_list2\n", "Method: List comprehension\n", "Memory usage:\t\t 0.148078 MB \n", "Peak memory usage:\t 3.765826 MB \n", "Time elapsed is seconds: 0.100013\n", "----------------------------------------\n", "None\n", "Function: make_list3\n", "Method: Append\n", "Memory usage:\t\t 0.000000 MB \n", "Peak memory usage:\t 3.617236 MB \n", "Time elapsed is seconds: 0.107031\n", "----------------------------------------\n", "None\n", "Function: make_list4\n", "Method: Concatenation\n", "Memory usage:\t\t 0.147821 MB \n", "Peak memory usage:\t 4.540657 MB \n", "Time elapsed is seconds: 48.821504\n", "----------------------------------------\n", "None\n" ] } ], "source": [ "print(make_list1())\n", "print(make_list2())\n", "print(make_list3())\n", "print(make_list4())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Felhasználás 9\n", "\n", "Saját függvény definiálható egy osztállyal is:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "class MyFunc:\n", "\n", " \n", " def __call__(self, *args, **kwargs):\n", " return 12" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "f=MyFunc()" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "12" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f()" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "import requests\n", "\n", "\n", "class LimitQuery:\n", "\n", " def __init__(self, func):\n", " self.func = func\n", " self.count = 0\n", "\n", " def __call__(self, *args, **kwargs):\n", " self.limit = args[0]\n", " if self.count < self.limit:\n", " self.count += 1\n", " return self.func(*args, **kwargs)\n", " else:\n", " print(f'No queries left. All {self.count} queries used.')\n", " return" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "$61635.11\n", "$61635.11\n", "$61635.11\n", "$61635.11\n", "$61635.11\n", "No queries left. All 5 queries used.\n", "None\n" ] } ], "source": [ "@LimitQuery\n", "def get_coin_price(limit):\n", " '''View the Bitcoin Price Index (BPI)'''\n", " \n", " url = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json')\n", "\n", " if url.status_code == 200:\n", " text = url.json()\n", " return f\"${float(text['bpi']['USD']['rate_float']):.2f}\"\n", "\n", "\n", "print(get_coin_price(5))\n", "print(get_coin_price(5))\n", "print(get_coin_price(5))\n", "print(get_coin_price(5))\n", "print(get_coin_price(5))\n", "print(get_coin_price(5))" ] } ], "metadata": { "celltoolbar": "Slideshow", "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.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }