{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Функции в Python \n", "\n", "Полную лекцию (автор – И.В.Щуров) можно найти [здесь](http://nbviewer.math-hse.info/github/ischurov/pythonhse/blob/master/Lecture%204.ipynb). В этом файле представлены материалы занятия, кратко освещающего ключевые моменты при написании функций в предположении о том, что студентами прослушана глава *Writing your own functions* курса *Python Data Science Toolbox (Part 1)* на платформе DataCamp.\n", "\n", "**План**\n", "\n", "1. Создание и описание функции.\n", "2. Разница между `return` и `print()`.\n", "3. Число аргументов и возвращаемых элементов.\n", "4. Функция с произвольным числом аргументов и `*`.\n", "5. Задание аргументов по умолчанию." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Создание и описание функции" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Создание функции начинается с ключевого слова (оператора) `def`, за которым следует название функции и перечень аргументов функции в круглых скобках. Во избежание путаницы и конфликта имен, нежелательно давать своим функциям названия, совпадающие с названиями уже существующих в Python функций. \n", "\n", "2. Хороший тон – добавлять специальную строку с описанием функции (*docstring*). Такая строка указывается между `\"\"\"` и `\"\"\"` и обычно содержит информацию, какие аргументы функция принимает на вход, что она с ними делает и что возвращает на выходе. Документацию к написанной функции можно увидеть, если запросить `help()` в том же файле.\n", "\n", "3. Строки внутри конструкции `def` (после двоеточия Python автоматически добавляет отступ) называются телом функции. \n", "\n", "4. Результат, который возвращает функция, указывается в строке с оператором `return`. Обычно `return` у функции один, в конце, но технически их может несколько (не очень хорошо с точки зрения стиля, но иногда допустимо).\n", "\n", "Посмотрим на пример функции для вычисления модуля (абсолютного значения) числа:\n", "\n", "* название: `my_module`;\n", "* аргумент: число `x` ;\n", "* результат: число `m`, модуль числа `x`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "def my_module(x):\n", " \"\"\"\n", " Parameters: x is a number.\n", " Returns an absolute value of x.\n", " \"\"\"\n", " if x >= 0:\n", " m = x\n", " else:\n", " m = -x\n", " return m" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function my_module in module __main__:\n", "\n", "my_module(x)\n", " Parameters: x is a number.\n", " Returns an absolute value of x.\n", "\n" ] } ], "source": [ "# посмотрим на документацию\n", "help(my_module) " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# применим функцию\n", "my_module(-7)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.5" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_module(2.5) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "А вот та же функция с двумя `return` (не пришлось создавать переменную `m`):" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def my_module(x):\n", " \"\"\"\n", " Parameters: x is a number.\n", " Returns an absolute value of x.\n", " \"\"\"\n", " if x >= 0:\n", " return x\n", " else:\n", " return -x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Сохраним результат применения функции в `res` и проверим, что все сохранилось:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "res = my_module(-9)\n", "res" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Разница между `return` и `print()`\n", "\n", "Если в функции есть `return`, она сохраняет возвращаемый результат, и к нему впоследствии можно обратиться. Если в функции нет `return`, а есть только `print()`, результат «молча» выводится на экран, и результат применения функции не сохраняется. \n", "\n", "Напишем функцию `my_module2()`, заменим `return` на `print()` и проверим, что получится. " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def my_module2(x):\n", " \"\"\"\n", " Parameters: x is a number.\n", " Returns an absolute value of x.\n", " \"\"\"\n", " if x >= 0:\n", " m = x\n", " else:\n", " m = -x\n", " print(m)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9\n" ] } ], "source": [ "# применим функцию к -9\n", "# результат выводится на экран\n", "\n", "res2 = my_module2(-9) " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "res2 # но в res2 ничего не сохранилось!" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "None\n" ] } ], "source": [ "# посмотрим более внимательно\n", "# в res2 пустое значение None (так как нет return)\n", "\n", "print(res2) " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9\n" ] } ], "source": [ "# с res, результатом my_module() с return,\n", "# все хорошо\n", "\n", "print(res) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Число аргументов и возвращаемых элементов\n", "\n", "1. Функция может принимать на вход и возвращать разные типы аргументов (число, строка, список, список списков, кортеж, словарь и так далее).\n", "\n", "2. Функция может принимать на вход и возвращать несколько аргументов. \n", "\n", "3. Если переменные с результатами перечислены через запятую после `return`, они автоматически будут объединены в кортеж.\n", "\n", "Напишем функцию `my_power()`, которая принимает на вход два целых числа и возвращает пару чисел $a^b$ и $b^a$." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "def my_power(a, b):\n", " return a**b, b**a" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(49, 128)" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# применяем и видим кортеж (tuple) из двух чисел\n", "\n", "my_power(7, 2) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Функция с произвольным числом аргументов и `*`\n", "\n", "Если мы хотим, чтобы функция умела работать с произвольным набором аргументов (например, набор чисел любой длины, перечисленных через запятую), можем воспользоваться оператором `*`, который добавит вокруг перечисленных аргументов круглые скобки, то есть объединит все в кортеж.\n", "\n", "Пример с использованием `*`: функция `my_sum()`, которая принимает на вход сколько угодно чисел (`nums`, могли подставить сюда любое название) и считает их сумму." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def my_sum(*nums):\n", " return sum(nums) " ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "18" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(2, 7, 8, 1, 0) # просто перечисляем числа через запятую" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Еще пример с использованием `*`: функция `my_text()`, которая принимает на вход любое количество слов, а затем склеивает их в одну строку:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def my_text(*words):\n", " return \" \".join(words) " ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'one two three'" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_text(\"one\", \"two\", \"three\") " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Задание аргументов по умолчанию\n", "\n", "При создании функции можно задать значения аргументов по умолчанию – значения аргументов, которые будут использоваться, если пользователь забыл их указать. Многие функции Python имеют значения аргументов по умолчанию. \n", "\n", "**Пример 1.** Если в функции `round()` мы пишем только одно число, Python округляет это число до целого (использует значение аргумента по умолчанию, `ndigits=None`). " ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "round(7.3) # округляет до целого" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on built-in function round in module builtins:\n", "\n", "round(number, ndigits=None)\n", " Round a number to a given precision in decimal digits.\n", " \n", " The return value is an integer if ndigits is omitted or None. Otherwise\n", " the return value has the same type as the number. ndigits may be negative.\n", "\n" ] } ], "source": [ "# смотрим в документации на аргументы\n", "\n", "help(round) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Пример 2.** Если в функции `print()` мы указываем только объекты, которые нужно вывести на экран, Python использует значения аргументов *разделитель* (`sep`) и *конец строки* (`end`) по умолчанию: в качестве разделителя используется пробел, а на конце строки добавляется переход на новую строку." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a b c\n" ] } ], "source": [ "print(\"a\", \"b\", \"c\")" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on built-in function print in module builtins:\n", "\n", "print(...)\n", " print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n", " \n", " Prints the values to a stream, or to sys.stdout by default.\n", " Optional keyword arguments:\n", " file: a file-like object (stream); defaults to the current sys.stdout.\n", " sep: string inserted between values, default a space.\n", " end: string appended after the last value, default a newline.\n", " flush: whether to forcibly flush the stream.\n", "\n" ] } ], "source": [ "help(print)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В своей функции тоже можно задать значение аргумента по умолчанию. Напишем простую функцию `hello()`, которая будет приветствовать пользователя на разных языках." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "def hello(name, lang):\n", " if lang == \"ru\":\n", " print(\"Привет,\", name)\n", " if lang == \"en\":\n", " print(\"Hello,\", name)\n", " if lang == \"fr\":\n", " print(\"Bonjours,\", name) " ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, Alla\n" ] } ], "source": [ "# все хорошо\n", "hello(\"Alla\", \"en\") " ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "hello() missing 1 required positional argument: 'lang'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\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 1\u001b[0m \u001b[0;31m# забыли второй аргумент – ошибка\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mhello\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Alla\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: hello() missing 1 required positional argument: 'lang'" ] } ], "source": [ "# забыли второй аргумент – ошибка\n", "hello(\"Alla\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Поправим: в качестве языка по умолчанию выставим русский. Теперь, если при вызове функции мы забудем указать язык, программа не выдаст ошибку, а автоматически выберет русский:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "def hello(name, lang=\"ru\"):\n", " if lang == \"ru\":\n", " print(\"Привет,\", name)\n", " if lang == \"en\":\n", " print(\"Hello,\", name)\n", " if lang == \"fr\":\n", " print(\"Bonjours,\", name) " ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Привет, Alla\n" ] } ], "source": [ "hello(\"Alla\") " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если пользователь укажет другой язык, естественно, функция будет приветствовать его на нем:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, Alla\n" ] } ], "source": [ "hello(\"Alla\", \"en\") " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Важное дополнение:** аргументы со значениями по умолчанию всегда записываются в `def` после «обязательных» аргументов. Если записать их в другом порядке, получим ошибку синтаксиса:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "non-default argument follows default argument (, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m def hello(lang=\"ru\", name):\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m non-default argument follows default argument\n" ] } ], "source": [ "def hello(lang=\"ru\", name):\n", " if lang == \"ru\":\n", " print(\"Привет,\", name)\n", " if lang == \"en\":\n", " print(\"Hello,\", name)\n", " if lang == \"fr\":\n", " print(\"Bonjours,\", name) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Вообще для функций порядок аргументов важен, если мы указываем их без названия. Если мы знаем, что на первом месте в `hello()` должно стоять имя, а на втором – язык, это нужно учитывать при использовании функции. Если мы поменяем их местами, мы либо получим ошибку, либо функция не сработает, как нужно:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "# ничего не печатает, так как функция понимает, что\n", "# Alla – это язык, кода для такого условия не находит\n", "\n", "hello(\"en\", \"Alla\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Но, если названия аргументов записывать явно, порядок уже не важен:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Bonjours, Alla\n" ] } ], "source": [ "hello(lang=\"fr\", name=\"Alla\")" ] } ], "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.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }