{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Funkcje - elementy zaawansowane\n", "\n", "## Funkcje jako obiekty\n", "\n", "Funkcje są w Pythonie “first-class objects”. Oznacza to, że funkcje:\n", "\n", "* można przekazywać jako argument do innych funkcji, np. do funkcji print,\n", "* mogą być rezultatem działania innej funkcji, np. dekoratory,\n", "* mogą być dynamicznie tworzone, np. funkcje zagnieżdżone." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo is happening\n" ] } ], "source": [ "def foo():\n", " print(\"foo is happening\")\n", "\n", "foo()" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "function" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(foo)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bar = foo\n", "\n", "id(bar) == id(foo)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(foo)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(foo)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Atrybuty funkcji" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "W szczególności, funkcje są instancjami typu function i mają atrybuty:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "function" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo.__class__" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'foo'" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo.__name__" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'foo'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bar.__name__" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Klasy jako funkcje" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "class CallableClass:\n", " def __init__(self):\n", " self._counter = 0\n", " \n", " def __call__(self):\n", " self._counter += 1\n", " print(\"You have called me {0} times\".format(self._counter))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "You have called me 1 times\n" ] } ], "source": [ "callable_object = CallableClass()\n", "callable_object()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "You have called me 2 times\n" ] } ], "source": [ "callable_object()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Preferowane jest tworzenie i używanie zwykłych funkcji. Później zawsze istnieje możliwość przekształcenia takiej funkcji w instancję klasy z metodą `__call__`." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Wywoływanie funkcji\n", "\n", "`callable` jest wbudowaną funkcją - nie trzeba jej importować, jest zawsze dostępna. Pozwala sprawdzić, czy dany obiekt da się wywołać (czy jest funkcją lub klasą lub instancją klasy z metodą `__call__`):" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "callable(foo)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "callable(callable_object)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 2\n", "callable(x)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "W Pythonie 3.0 i 3.1 usunięto funkcję `callable`, jednak w Pythonie 3.2+ stała się ponownie wbudowaną funkcją. Jeżeli Twój kod musi działać także pod Pythonem 3.0, 3.1, wówczas należy użyć jednego z dwóch poniższych idiomów:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hasattr(foo, '__call__')" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import collections.abc\n", "\n", "isinstance(foo, collections.abc.Callable)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Funkcje zagnieżdżone\n", "\n", "Funkcje można zagnieżdżać, to znaczy zdefiniować jedną funkcję (wewnętrzną) w ciele drugiej funkcji (zewnętrznej).\n", "\n", "Ponieważ funkcje są obiektami (*first-class citizen*), funkcja wewnętrzna może zostać zwrócona przez funkcję zewnętrzną.\n", "\n", "Jest to szczególnie użyteczne przy tworzeniu *dekoratorów*.\n", "\n", "Warto zauważyć, że w poniższym przykładzie funkcja `add` jest tworzona dynamicznie przy każdym wywołaniu funkcji `bind_add`.\n", "\n", "Oznacza to, że przy każdym wywołaniu `bind_add` zwracana jest inna funkcja, mającą własną tożsamość (*identity*), co można sprawdzić przy pomocy wbudowanej funkcji `id`.\n" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "def bind_add(a):\n", " def add(b):\n", " return a + b\n", " return add" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "140380393986656" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add_1 = bind_add(1)\n", "type(add_1)\n", "id(add_1)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add_1(5)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "140380393985216" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add_42 = bind_add(42)\n", "id(add_42)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "100" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add_42(58)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Funkcje wyższego rzędu\n", "\n", "Funkcja wyższego rzędu wymaga podania jako argumentu innej funkcji lub zwraca funkcję jako rezultat. Przykładową funkcją \n", "wyższego rzędu w Pythonie jest funkcja `sorted`. Opcjonalny argument ``key`` umożliwia przekazanie funkcji, która będzie wywołana dla każdego sortowanego elementu\n" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['mp3', 'ipod', 'ipad', 'pendrive', 'smartwatch']" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "gadgets = ['mp3', 'smartwatch', 'ipod', 'pendrive', 'ipad']\n", "sorted(gadgets, key=len)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Często jako argumenty funkcji wyższego rzędu przekazywane są wyrażenia lambda." ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(5, 'five'), (4, 'four'), (1, 'one'), (3, 'three'), (2, 'two'), (0, 'zero')]" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lst_numbers = [(0, \"zero\"), (1, \"one\"), (2, \"two\"), (3, \"three\"), (4, \"four\"), (5, \"five\")]\n", "sorted(lst_numbers, key=lambda item : item[1])" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Wyrażenia lambda\n", "\n", "Wyrażenia lambda pozwalają na zwięzłe stworzenie funkcji bez nazwy, tzw. funkcji anonimowej." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add = lambda a, b: a + b\n", "\n", "add(2, 3)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "function" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(add)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "callable(add)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "''" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add.__name__" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "W ciele funkcji nie można umieścić instrukcji, a jedynie pojedyncze wyrażenie (np. ``a + b``), które jest rezultatem takiej funkcji.\n", "\n", "Dlatego wyrażenia lambda najczęściej wykorzystuje się razem z funkcjami wyższego rzędu ``filter`` i ``map``.\n", "\n", "Każdy element z jakiejś kolekcji może zostać przekształcony za pomocą funkcji (``map``) albo przefiltrowany przy pomocy predykatu (``filter``)." ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, 8, 42, 665, 54]" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "numbers = [1, -3, 4, -5, 0, 8, 42, 665, 54, -65]\n", "positive_numbers = filter(lambda n: n > 0, numbers)\n", "list(positive_numbers)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 9, 16, 25, 0, 64, 1764, 442225, 2916, 4225]" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "squares = map(lambda n: n * n, numbers)\n", "list(squares)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Lepiej jest jednak zastąpić ``filter`` i ``map`` wyrażeniami listowymi lub generatorowymi, ponieważ wywoływanie funkcji w Pythonie jest związane z dużym narzutem czasowym.\n", "Użycie wyrażeń listowych lub generatorowych nie powoduje wielokrotnego wywoływania funkcji." ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, 8, 42, 665, 54]" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x for x in numbers if x > 0]" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 9, 16, 25, 0, 64, 1764, 442225, 2916, 4225]" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x * x for x in numbers]" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Zmienne lokalne, nielokalne i globalne\n", "\n", "Python korzysta z przestrzeni nazw (*namespace*), aby śledzić zmienne.\n", "Są to słowniki, których kluczami są nazwy zmiennych, a wartościami wartości tych zmiennych.\n", "W środku funkcji mamy dostępu do wielu przestrzeni nazw.\n", "\n", "Najważniejszą z nich jest lokalna przestrzeń nazw, która zawiera argumenty funkcji i lokalnie zdefiniowane zmienne.\n", "Zmienne z tej przestrzeni nie są widoczne na zewnątrz funkcji.\n", "\n", "Globalna przestrzeń nazw zawiera wszystkie zmienne zdefiniowane w module.\n", "Są to wszystkie zmienne, które nie są \"wcięte\".\n", "Funkcje i klasy to także obiekty, więc w tej przestrzeni nazw znajdują się również one.\n", "\n", "W przypadku funkcji zagnieżdżonych, w środku wewnętrznej funkcji możemy mieć do czynienia z przestrzenią nazw zewnętrznej funkcji.\n", "Nie jest to ani globalna, ani lokalna przestrzeń.\n", "\n", "Gdy odwołujemy się do zmiennej, Python musi zdecydować, z której przestrzeni ma skorzystać.\n", "Jeżeli próbujemy odczytać wartość zmiennej, wówczas wykorzystywana jest najbliższa przestrzeń, w której dana zmienna jest zadeklarowana.\n", "Najbliższą jest lokalna przestrzeń nazw, potem nielokalne i na końcu globalna.\n", "\n", "Jeżeli przypisujemy coś do zmiennej, to Python zakłada, że chcemy ją stworzyć w przestrzeni lokalnej, chyba że użyjemy słów kluczowych ``nonlocal`` lub ``global``." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "inner -2 -3 -4\n", "outer -2 -3 4\n", "global -2 4\n" ] } ], "source": [ "global_var = 2\n", "var = 4\n", "\n", "def outer():\n", " nonlocal_var = 3\n", " \n", " def inner():\n", " global global_var\n", " nonlocal nonlocal_var\n", " global_var = -2 # modyfikujemy zmienną globalną\n", " var = -4 # tworzymy zmienną lokalną niezależną od zmiennej globalnej var = 3\n", " nonlocal_var = -3 # modyfikujemy zmienną nielokalną\n", " print(\"inner\", global_var, nonlocal_var, var)\n", " \n", " inner()\n", " \n", " print(\"outer\", global_var, nonlocal_var, var)\n", "outer()\n", "\n", "print(\"global\", global_var, var)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Warto zauważyć, że decyzja o tym, która przestrzeń nazw zostanie wykorzystana, jest podejmowana już w czasie kompilacji funkcji:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "ename": "UnboundLocalError", "evalue": "local variable 'x' referenced before assignment", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mUnboundLocalError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mfoo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mfoo\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mfoo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'x' referenced before assignment" ] } ], "source": [ "x = 2\n", "\n", "def foo():\n", " print(x)\n", " x = 3\n", "\n", "foo()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "W powyższym przykładzie Python założył, że ``x`` jest zmienną lokalną, ponieważ w środku funkcji znajduje się przypisanie do tej zmiennej.\n", "``print(x)`` odwołuje się dalej do zmiennej lokalnej, a nie globalnej." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Parametry kontra argumenty" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def add(a, b):\n", " return a+b\n", "\n", "x = 3\n", "y = 2\n", "\n", "add(x, y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "``a`` i ``b`` są parametrami funkcji, natomiast ``x`` i ``y`` argumentami." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Parametry funkcji\n", "\n", "### Wprowadzenie\n", "\n", "W Pythonie rozróżniamy cztery różne typy parametrów:\n", "\n", "* normalne (*normal parameters*)\n", " mają nazwę i pozycję\n", "\n", "* nazwane (*keyword parameters*)\n", " mają nazwę i domyślną wartość\n", "\n", "* zmienne (*variable parameters*)\n", " poprzedzone gwiazdką \\*, mają pozycję\n", "\n", "* zmienne nazwane (*variable keyword parameters*)\n", " poprzedzone \\**, mają nazwę\n", "\n", "### Parametry normalne i nazwane\n" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ola 1995 Wrocław\n" ] } ], "source": [ "\n", "def generate_signature(person, year=2000, place=\"Paris\"):\n", " print(person, year, place)\n", " \n", "generate_signature(\"Ola\", 1995, \"Wrocław\")" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ala 2000 Paris\n" ] } ], "source": [ "generate_signature(\"Ala\")" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Olek 2000 New York\n" ] } ], "source": [ "generate_signature(\"Olek\", place=\"New York\")" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Alek 2010 Paris\n" ] } ], "source": [ "generate_signature(\"Alek\", year=2010)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Parametry zmienne (*args)\n", "\n", "Operator * służy do tworzenia funkcji akceptujących dowolną liczbę argumentów:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def my_sum(*numbers):\n", " total = 0\n", " for number in numbers:\n", " total += number\n", " return total\n", "\n", "my_sum(1, 2, 3, 4)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Parametry zmienne nazwane (**kwargs)\n", "\n", "Operator ** służy do tworzenia funkcji akceptujących dowolną liczbę argumentów nazwanych:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'a': '1999', 'b': '2000'}" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def dict_without_Nones(**kwargs):\n", " result = {}\n", " for k, v in kwargs.items():\n", " if v is not None:\n", " result[k] = v\n", " return result\n", "\n", "dict_without_Nones(a=\"1999\", b=\"2000\", c=None)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Parametry zmienne razem (*args, **kwargs)\n", "\n", "Funkcja może przyjmować jednocześnie parametry zmienne i zmienne nazwane:" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "(2, 3, 4, 5)\n", "{'ala': '1999', 'ola': '2000'}\n" ] } ], "source": [ "def multi(first, *args, **kwargs):\n", " print(first)\n", " print(args)\n", " print(kwargs)\n", "\n", "multi(1, 2, 3, 4, 5, ala=\"1999\", ola=\"2000\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Pułapki domyślnego atrybutu\n", "\n", "Domyślna wartość dla argumentów nazwanych jest wyliczana w momencie deklarowania funkcji.\n", "Wartość ta nie jest ponownie wyliczana przy wywoływaniu funkcji.\n", "Zachowanie to nie jest intuicyjne:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [], "source": [ "def its_a_trap(item, seq=[]):\n", " seq.append(item)\n", " print(seq)" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1]\n" ] } ], "source": [ "its_a_trap(1)" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2]\n" ] } ], "source": [ "its_a_trap(2)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Dlatego jako wartości domyślnych należy używać tylko niemodyfikowalnych obiektów, takich jak prymitywne wartości (``0, True, None, 'string'`` itp.).\n", "\n", "Jeżeli wartość domyślna musi koniecznie być modyfikowalnym obiektem (np. listą), wówczas należy domyślnie użyć ``None`` i w środku funkcji przypisać pożądaną wartość:" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [], "source": [ "def now_its_fine(item, seq=None):\n", " if seq is None:\n", " seq = []\n", " seq.append(item)\n", " print(seq)" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1]\n" ] } ], "source": [ "now_its_fine(1)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2]\n" ] } ], "source": [ "now_its_fine(2)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Adnotacje funkcji\n", "\n", "W Pythonie 3 wprowadzono składnię pozwalającą na powiązanie argumentów funkcji i metod oraz zwracaną wartość z dowolnym obiektem.\n", "W szczególności, dla każdego:" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "def clip(text:str, max_len:'int > 0'=80) -> str:\n", " return text[:max_len]" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Adnotacje funkcji (*function annotations*) są nietypową funkcjonalnością, ponieważ nie określono, do czego konkretnie takie adnotacje mogą zostać użyte.\n", "\n", "Adnotacje są dostępne jako specjalny atrybut:" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'text': str, 'max_len': 'int > 0', 'return': str}" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "clip.__annotations__" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Przykładowe zastosowanie to dodanie informacji o typach (statyczne typowanie).\n", "\n", "Dzięki temu narzędzia takie jak ``mypy`` mogą zanalizować kod, sprawdzić zgodność typów i w ten sposób wykryć ewentualne błędy jeszcze przed uruchomieniem kodu.\n", "\n", "## Atrybuty funkcji\n", "\n", "W Pythonie wszystko jest obiektem, także funkcje.\n", "\n", "Funkcje posiadają specjalne atrybuty ułatwiające ich introspekcję:" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [], "source": [ "def foo(arg, kwarg=42, *, kwarg2=43):\n", " '''docstring'''\n", " return arg + kwarg + kwarg2" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'foo'" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo.__name__" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'docstring'" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo.__doc__" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(42,)" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo.__defaults__" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'kwarg2': 43}" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo.__kwdefaults__\n" ] } ], "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.8" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }