Функции python 2. Создание функции Python. Анонимные функции, инструкция lambda

5 августа 2008 в 16:14

Основы Python - кратко. Часть 5. Определение функций, основы.

  • Python

Начав писать главу про ООП, понял что совсем забыл освятить такой большой и нужный раздел Пайтона как функции. Тема это большая и обширная, потому, чтобы не сильно растягивать паузу между уроками, решил разделить ее на 2 части. Сначала расскажу основы, потом уже углубленные особенности Пайтоновского функциестроения.

Функции в Пайтоне объявляются не просто, а очень просто. Вот пример самой простой:

Def empty_func(): pass
Начинается объявление с ключевого слова def, что как не сложно догадаться является сокращением от define. После него идет имя функции. После имени в круглых скобках задается список параметров, в данном случае отсутствующих.
Тело функции пишется с отступом со следующей строки. учтите, что в Пайтоне функции с пустым телом запрещены, потому в качестве тела приведенной выше функции используется «пустой оператор» pass.
Теперь рассмотрим пример посерьезнее.

Def safe_div(x, y): """Do a safe division:-) for fun and profit""" if y != 0: z = x / y print z return z else: print "Yippie-kay-yay, motherf___er!"
В этом примере есть несколько нововведений. первое, что бросается в глаза - это строка документации (docstring), идущая сразу после тела функции.
Обычно эта строка занимает не одну строку исходного текста (простите за каламбур) и потому задается в тройных кавычках. Она предназначена для описания функции, ее предназначения, параметров и т.п. Все хорошие ИДЕ умеют с этой строкой работать. Получить к ней доступ можно и из самой программы, используя свойство __doc__:

Print safe_div.__doc__
Этим свойством (да, да, именно свойством, в Пайтоне даже функции на самом деле - классы) удобно пользоваться во время сеансов работы интерактивной консоли.
>>> from ftplib import FTP >>> print FTP.__doc__ An FTP client class. To create a connection, call the class using these argument: host, user, passwd, acct These are all strings, and have default value "". Then use self.connect() with optional host and port argument. # дальнейшее почикано мною:-)
Вернемся к нашей исходной функции. Суть ее очень проста, она принимает 2 параметра: х и у. Если у не равен 0, она делит х на у, выводит результат на экран и возвращает свое частное в виде результата. Результат функции возвращают с помощью команды return. Благодаря механизму кортежей, описанному в прошлом уроке, функции в Пайтоне могут возвращать одновременно множество объектов.
Если же делитель все-таки равен нулю, функция выводит сообщение об ошибке. Неверно было бы предположить что в этом случае функция ничего не вернет. Правильнее будет сказать что функция вернет «ничего»:) Иначе говоря, если в функции отсутствует оператор return, или же он вызван без параметров, то функция возвращает специальное значение None. В этом легко убедиться вызвав что-то типа print safe_div(10, 0).

Вот пример слегка посложнее, он взят из доклада-презентации Гвидо ван Россума.

Def gcd(a, b): "Нахождение НОД" while a != 0: a,b = b%a,a # параллельное определение return b
Данная функция находит наибольший общий делитель двух чисел.

В общем, следует учитывать, что параметры в функции Пайтоном передаются по ссылке. Еще одним, возможно нетривиальным фактом к которому придется привыкать - является тот факт что сами функции являются значением, которое можно присваивать. Если воспользоваться нашей функцией safe_div для дальнейших экспериментов, то можно написать следующий код.

Mystic_function = safe_div print mystic_function(10, 4)
Вот на этот раз и все, «за бортом» осталось еще много аспектов определения функций в Пайтоне, которые будут освещены в следующий раз.

Упражнения для проверки.
1. На основе существующей функции нахождения НОД, напишите функцию поиска НОК двух чисел.
2. Напишите подпрограмму табулирования функции, переданной в качестве аргумента. Так же аргументами задается начальное, конечное значение и шаг табуляции.

PS кстати, каков оптимальный объем «урока»? Что лучше - реже выходящие большие главы, или «лучше меньше да чаще».

Существует большое количество публикаций, посвящённых реализациям концепций функционального программирования на языке Python, но большая часть этих материалов написана одним автором - Девидом Мертцом (David Mertz). Кроме того, многие из этих статей уже устарели и разнесены по различным сетевым ресурсам. В этой статье мы попробуем снова обратиться к этой теме, чтобы освежить и упорядочить доступную информацию, особенно учитывая большие различия, имеющиеся между версиями Python линии 2 и линии 3.

Функции в Python

Функции в Python определяются 2-мя способами: через определение def или через анонимное описание lambda . Оба этих способа определения доступны, в той или иной степени, и в некоторых других языках программирования. Особенностью Python является то, что функция является таким же именованным объектом, как и любой другой объект некоторого типа данных, скажем, как целочисленная переменная. В листинге 1 представлен простейший пример (файл func.py из архива python_functional.tgz

Листинг 1. Определения функций
#!/usr/bin/python # -*- coding: utf-8 -*- import sys def show(fun, arg): print("{} : {}".format(type(fun), fun)) print("arg={} => fun(arg)={}".format(arg, fun(arg))) if len(sys.argv) > 1: n = float(sys.argv[ 1 ]) else: n = float(input("число?: ")) def pow3(n): # 1-е определение функции return n * n * n show(pow3, n) pow3 = lambda n: n * n * n # 2-е определение функции с тем же именем show(pow3, n) show((lambda n: n * n * n), n) # 3-е, использование анонимного описание функции

При вызове всех трёх объектов-функций мы получим один и тот же результат:

$ python func.py 1.3 : arg=1.3 => fun(arg)=2.197 : at 0xb7662bc4> arg=1.3 => fun(arg)=2.197 : at 0xb7662844> arg=1.3 => fun(arg)=2.197

Ещё более отчётливо это проявляется в Python версии 3, в которой всё является классами (в том числе, и целочисленная переменная), а функции являются объектами программы, принадлежащими к классу function :

$ python3 func.py 1.3 : arg=1.3 => fun(arg)=2.1970000000000005 : at 0xb745432c> arg=1.3 => fun(arg)=2.1970000000000005 : at 0xb74542ec> arg=1.3 => fun(arg)=2.1970000000000005

Примечание . Существуют ещё 2 типа объектов, допускающих функциональный вызов - функциональный метод класса и функтор, о которых мы поговорим позже.

Если функциональные объекты Python являются такими же объектами, как и другие объекты данных, значит, с ними можно и делать всё то, что можно делать с любыми данными:

  • динамически изменять в ходе выполнения;
  • встраивать в более сложные структуры данных (коллекции);
  • передавать в качестве параметров и возвращаемых значений и т.д.

На этом (манипуляции с функциональными объектами как с объектами данных) и базируется функциональное программирование. Python, конечно, не является настоящим языком функционального программирования, так, для полностью функционального программирования существуют специальные языки: Lisp, Planner, а из более свежих: Scala, Haskell. Ocaml, ... Но в Python можно "встраивать" приёмы функционального программирования в общий поток императивного (командного) кода, например, использовать методы, заимствованные из полноценных функциональных языков. Т.е. "сворачивать" отдельные фрагменты императивного кода (иногда достаточно большого объёма) в функциональные выражения.

Временами спрашивают: «В чём преимущества функционального стиля написания отдельных фрагментов для программиста?». Основным преимуществом функционального программирования является то, что после однократной отладки такого фрагмента в нём при последующем многократном использовании не возникнут ошибки за счёт побочных эффектов, связанных с присвоениями и конфликтом имён.

Достаточно часто при программировании на Python используют типичные конструкции из области функционального программирования, например:

print ([ (x,y) for x in (1, 2, 3, 4, 5) \ for y in (20, 15, 10) \ if x * y > 25 and x + y < 25 ])

В результате запуска получаем:

$ python funcp.py [(2,20), (2,15), (3,20), (3,15), (3,10), (4,20), (4,15), (4,10), (5,15), (5,10)]

Функции как объекты

Создавая объект функции оператором lambda , как было показано в листинге 1, можно привязать созданный функциональный объект к имени pow3 в точности так же, как можно было бы привязать к этому имени число 123 или строку "Hello!" . Этот пример подтверждает статус функций как объектов первого класса в Python. Функция в Python - это всего лишь ещё одно значение, с которым можно что-то сделать.

Наиболее частое действие, выполняемое с функциональными объектами первого класса, - это передача их во встроенные функции высшего порядка: map() , reduce() и filter() . Каждая из этих функций принимает объект функции в качестве своего первого аргумента.

  • map() применяет переданную функцию к каждому элементу в переданном списке (списках) и возвращает список результатов (той же размерности, что и входной);
  • reduce() применяет переданную функцию к каждому значению в списке и ко внутреннему накопителю результата, например, reduce(lambda n,m: n * m, range(1, 10)) означает 10! (факториал);
  • filter() применяет переданную функцию к каждому элементу списка и возвращает список тех элементов исходного списка, для которых переданная функция вернула значение истинности.

Комбинируя эти три функции, можно реализовать неожиданно широкий диапазон операций потока управления, не прибегая к императивным утверждениям, а используя лишь выражения в функциональном стиле, как показано в листинге 2 (файл funcH.py из архива python_functional.tgz

Листинг 2. Функции высших порядков Python
#!/usr/bin/python # -*- coding: utf-8 -*- import sys def input_arg(): global arg arg = (lambda: (len(sys.argv) > 1 and int(sys.argv[ 1 ])) or \ int(input("число?: ")))() return arg print("аргумент = {}".format(input_arg())) print(list(map(lambda x: x + 1, range(arg)))) print(list(filter(lambda x: x > 4, range(arg)))) import functools print("{}! = {}".format(arg, functools.reduce(lambda x, y: x * y, range(1, arg))))

Примечание. Этот код несколько усложнён по сравнению с предыдущим примером из-за следующих аспектов, связанных с совместимостью Python версий 2 и 3:

  • Функция reduce() , объявленная как встроенная в Python 2, в Python 3 была вынесена в модуль functools и её прямой вызов по имени вызовет исключение NameError , поэтому для корректной работы вызов должен быть оформлен как в примере или включать строку: from functools import *
  • Функции map() и filter() в Python 3 возвращают не список (что уже показывалось при обсуждении различий версий), а объекты-итераторы вида:

Для получения всего списка значений для них вызывается функция list() .

Поэтому такой код сможет работать в обеих версиях Python:

$ python3 funcH.py 7 аргумент = 7 7! = 720

Если переносимость кода между различными версиями не требуется, то подобные фрагменты можно исключить, что позволит несколько упростить код.

Рекурсия

В функциональном программировании рекурсия является основным механизмом, аналогично циклам в итеративном программировании.

В некоторых обсуждениях по Python неоднократно приходилось встречаться с заявлениями, что в Python глубина рекурсии ограничена "аппаратно", и поэтому некоторые действия реализовать невозможно в принципе. В интерпретаторе Python действительно по умолчанию установлено ограничение глубины рекурсии, равным 1000, но это численный параметр, который всегда можно переустановить, как показано в листинге 3 (полный код примера можно найти в файле fact2.py из архива python_functional.tgz

Листинг 3. Вычисление факториала с произвольной глубиной рекурсии
#!/usr/bin/python # -*- coding: utf-8 -*- import sys arg = lambda: (len(sys.argv) > 1 and int(sys.argv[ 1 ])) or \ int(input("число?: ")) factorial = lambda x: ((x == 1) and 1) or x * factorial(x - 1) n = arg() m = sys.getrecursionlimit() if n >= m - 1: sys.setrecursionlimit(n + 2) print("глубина рекурсии превышает установленную в системе {}, переустановлено в {}".\ format(m, sys.getrecursionlimit())) print("n={} => n!={}".format(n, factorial(n))) if sys.getrecursionlimit() > m: print("глубина рекурсии восстановлена в {}".format(m)) sys.setrecursionlimit(m)

Вот как выглядит исполнение этого примера в Python 3 и в Python2 (правда на самом деле полученное число вряд ли поместится на один экран терминала консоли):

$ python3 fact2.py 1001 глубина рекурсии превышает установленную в системе 1000, переустановлено в 1003 n=1001 => n!=4027.................................................0000000000000 глубина рекурсии восстановлена в 1000

Несколько простейших примеров

Выполним несколько простейших трансформаций привычного императивного кода (командного, операторного) для превращения его отдельных фрагментов в функциональные. Сначала заменим операторы ветвления логическими условиями, которые за счёт "отложенных" (lazy, ленивых) вычислений позволяют управлять выполнением или невыполнением отдельных ветвей кода. Так, императивная конструкция:

if <условие>: <выражение 1> else: <выражение 2>

Полностью эквивалентна следующему функциональному фрагменту (за счёт "отложенных" возможностей логических операторов and и or ):

# функция без параметров: lambda: (<условие> and <выражение 1>) or (<выражение 2>)

В качестве примера снова используем вычисление факториала. В листинге 4 приведен функциональный код для вычисления факториала (файл fact1.py в архиве python_functional.tgz в разделе "Материалы для скачивания"):

Листинг 4. Операторное (императивное) определение факториала
#!/usr/bin/python # -*- coding: utf-8 -*- import sys def factorial(n): if n == 1: return 1 else: return n * factorial(n - 1) if len(sys.argv) > 1: n = int(sys.argv[ 1 ]) else: n = int(input("число?: ")) print("n={} => n!={}".format(n, factorial(n)))

Аргумент для вычисления извлекается из значения параметра командной строки (если он есть) или вводится с терминала. Первый вариант изменения, показанный выше, уже применяется в листинге 2, где на функциональные выражения были заменены:

  • определение функции факториала: factorial = lambda x: ((x == 1) and 1) or x * factorial(x - 1)
  • запрос на ввод значения аргумента с консоли терминала: arg = lambda: (len(sys.argv) > 1 and int(sys.argv[ 1 ])) or \ int(input("число?: ")) n = arg()

В файле fact3.py появляется ещё одно определение функции, сделанное через функцию высшего порядка reduсe() :

factorial = factorial = lambda z: reduce(lambda x, y: x * y, range(1, z + 1))

Здесь же мы упростим также и выражение для n , сведя его к однократному вызову анонимной (не именованной) функции:

n = (lambda: (len(sys.argv) > 1 and int(sys.argv[ 1 ])) or \ int(input("число?: ")))()

Наконец, можно заметить, что присвоение значения переменной n требуется только для её использования в вызове print() для вывода этого значения. Если мы откажемся и от этого ограничения, то всё приложение выродится в один функциональный оператор (см. файл fact4.py в архиве python_functional.tgz в разделе "Материалы для скачивания"):

from sys import * from functools import reduce print("вычисленный факториал = {}".format(\ (lambda z: reduce(lambda x, y: x * y, range(1, z + 1))) \ ((lambda: (len(argv) > 1 and int(argv[ 1 ])) or \ int(input("число?: ")))())))

Этот единственный вызов внутри функции print() и представляет всё приложение в его функциональном варианте:

$ python3 fact4.py число?: 5 вычисленный факториал = 120

Читается ли этот код (файл fact4.py) лучше, чем императивная запись (файл fact1.py)? Скорее нет, чем да. В чём же тогда его достоинство? В том, что при любых изменениях окружающего его кода, нормальная работа этого фрагмента сохранится, так как отсутствует риск побочных эффектов из-за изменения значений используемых переменных.

Функции высших порядков

При функциональном стиле программирования стандартной практикой является динамическая генерация функционального объекта в процессе исполнения кода, с его последующим вызовом в том же коде. Существует целый ряд областей, где подобная техника может оказаться полезной.

Замыкание

Одно из интересных понятий функционального программирования - это замыкания (closure). Эта идея оказалась настолько заманчивой для многих разработчиков, что была реализована даже в некоторых нефункциональных языках программирования (Perl). Девид Мертц приводит следующее определение замыкания: "Замыкание - это процедура вместе с привязанной к ней совокупностью данных" (в противовес объектам в объектном программировании, как: "данные вместе с привязанным к ним совокупностью процедур").

Смысл замыкания состоит в том, что определение функции "замораживает" окружающий её контекст на момент определения . Это может делаться различными способами, например, за счёт параметризации создания функции, как показано в листинге 5 (файл clos1.py в архиве python_functional.tgz в разделе "Материалы для скачивания"):

Листинг 5. Создание замыкания
# -*- coding: utf-8 -*- def multiplier(n): # multiplier возвращает функцию умножения на n def mul(k): return n * k return mul mul3 = multiplier(3) # mul3 - функция, умножающая на 3 print(mul3(3), mul3(5))

Вот как срабатывает такая динамически определённая функция:

$ python clos1.py (9, 15) $ python3 clos1.py 9 15

Другой способ создания замыкания - это использование значения параметра по умолчанию в точке определения функции, как показано в листинге 6 (файл clos3.py из архива python_functional.tgz в разделе "Материалы для скачивания"):

Листинг 6. Другой способ создания замыкания
n = 3 def mult(k, mul = n): return mul * k n = 7 print(mult(3)) n = 13 print(mult(5)) n = 10 mult = lambda k, mul=n: mul * k print(mult(3))

Никакие последующие присвоения значений параметру по умолчанию не приведут к изменению ранее определённой функции, но сама функция может быть переопределена:

$ python clos3.py 9 15 30

Частичное применение функции

Частичное применение функции предполагает на основе функции N переменных определение новой функции с меньшим числом переменных M < N , при этом остальные N - M переменных получают фиксированные "замороженные" значения (используется модуль functools ). Подобный пример будет рассмотрен ниже.

Функтор

Функтор - это не функция, а объект класса, в котором определён метод с именем __call__() . При этом, для экземпляра такого объекта может применяться вызов, точно так же, как это происходит для функций. В листинге 7 (файл part.py из архива python_functional.tgz в разделе "Материалы для скачивания") демонстрируется использование замыкания, частичного определения функции и функтора, приводящих к получению одного и того же результата.

Листинг 7. Сравнение замыкания, частичного определения и функтора
# -*- coding: utf-8 -*- def multiplier(n): # замыкания - closure def mul(k): return n * k return mul mul3 = multiplier(3) from functools import partial def mulPart(a, b): # частичное применение функции return a * b par3 = partial(mulPart, 3) class mulFunctor: # эквивалентный функтор def __init__(self, val1): self.val1 = val1 def __call__(self, val2): return self.val1 * val2 fun3 = mulFunctor(3) print("{} . {} . {}".format(mul3(5), par3(5), fun3(5)))

Вызов всех трёх конструкций для аргумента, равного 5, приведёт к получению одинакового результата, хотя при этом и будут использоваться абсолютно разные механизмы:

$ python part.py 15 . 15 . 15

Карринг

Карринг (или каррирование, curring) - преобразование функции от многих переменных в функцию, берущую свои аргументы по одному.

Примечание . Это преобразование было введено М. Шейнфинкелем и Г. Фреге и получило своё название в честь математика Хаскелла Карри, в честь которого также назван и язык программирования Haskell.

Карринг не относится к уникальным особенностям функционального программирования, так карринговое преобразование может быть записано, например, и на языках Perl или C++. Оператор каррирования даже встроен в некоторые языки программирования (ML, Haskell), что позволяет многоместные функции приводить к каррированному представлению. Но все языки, поддерживающие замыкания, позволяют записывать каррированные функции, и Python не является исключением в этом плане.

В листинге 8 представлен простейший пример с использованием карринга (файл curry1.py в архиве python_functional.tgz в разделе "Материалы для скачивания"):

Листинг 8. Карринг
# -*- coding: utf-8 -*- def spam(x, y): print("param1={}, param2={}".format(x, y)) spam1 = lambda x: lambda y: spam(x, y) def spam2(x) : def new_spam(y) : return spam(x, y) return new_spam spam1(2)(3) # карринг spam2(2)(3)

Вот как выглядят исполнение этих вызовов:

$ python curry1.py param1=2, param2=3 param1=2, param2=3

Заключение

В этой статье были представлены некоторые возможности языка Python, позволяющие применять его для написания программ, использующих стиль функционального программирования. Так, мы описали основные приёмы функционального программирования и показали примеры их реализации в Python. Как и в предыдущих статьях, примеры кода написаны таким образом, что могут успешно запускаться и исполняться в обеих версиях Python.

В следующей статье мы обсудим вопросы организации параллельного исполнения кода в среде Python.

Именные функции, инструкция def

Функция в python - объект, принимающий аргументы и возвращающий значение. Обычно функция определяется с помощью инструкции def.

Определим простейшую функцию:

Инструкция return говорит, что нужно вернуть значение. В нашем случае функция возвращает сумму x и y.

Теперь мы ее можем вызвать:

>>> add(1, 10)

>>> add("abc", "def")

Функция может быть любой сложности и возвращать любые объекты (списки, кортежи, и даже функции!):

>>> def newfunc(n):

Def myfunc(x):

Return x + n

Return myfunc

>>> new = newfunc(100) # new - это функция

>>> new(200)

Функция может и не заканчиваться инструкцией return, при этом функция вернет значениеNone:

>>> def func():

>>> print(func())

Аргументы функции

Функция может принимать произвольное количество аргументов или не принимать их вовсе. Также распространены функции с произвольным числом аргументов, функции с позиционными и именованными аргументами, обязательными и необязательными.

>>> def func(a, b, c=2): # c - необязательный аргумент

Return a + b + c

>>> func(1, 2) # a = 1, b = 2, c = 2 (по умолчанию)

>>> func(1, 2, 3) # a = 1, b = 2, c = 3

>>> func(a=1, b=3) # a = 1, b = 3, c = 2

>>> func(a=3, c=6) # a = 3, c = 6, b не определен

Traceback (most recent call last):

File "", line 1, in

TypeError: func() takes at least 2 arguments (2 given)

Функция также может принимать переменное количество позиционных аргументов, тогда перед именем ставится *:

>>> def func(*args):

>>> func(1, 2, 3, "abc")

(1, 2, 3, "abc")

>>> func(1)

Как видно из примера, args - это кортежиз всех переданных аргументов функции, и с переменной можно работать также, как и с кортежем.

Функция может принимать и произвольное число именованных аргументов, тогда перед именем ставится **:

>>> def func(**kwargs):

Return kwargs

>>> func(a=1, b=2, c=3)

{"a": 1, "c": 3, "b": 2}

>>> func(a="python")

В переменной kwargs у нас хранится словарь, с которым мы, опять-таки, можем делать все, что нам заблагорассудится.

Анонимные функции, инструкция lambda

Анонимные функции могут содержать лишь одно выражение, но и выполняются они быстрее. Анонимные функции создаются с помощью инструкции lambda. Кроме этого, их не обязательно присваивать переменной, как делали мы инструкцией def func():

>>> func = lambda x, y: x + y

>>> func(1, 2)

>>> func("a", "b")

>>> (lambda x, y: x + y)(1, 2)

>>> (lambda x, y: x + y)("a", "b")

lambda функции, в отличие от обычной, не требуется инструкция return, а в остальном, ведет себя точно так же:

>>> func = lambda *args: args

>>> func(1, 2, 3, 4)

19. Понятие рекурсии, реализация в языке Python

В программировании рекурсия - вызов функции (процедуры) из неё же самой, непосредственно (простая рекурсия) или через другие функции (сложная или косвенная рекурсия), например, функция вызывает функцию, а функция- функцию. Количество вложенных вызовов функции или процедуры называется глубиной рекурсии.

Проще сказать нельзя. Про рекурсии есть известная поговорка:

Чтобы понять рекурсию, нужно сперва понять рекурсию

Итак, питон позволяет работать с рекурсиями легко и непринужденно. Самый первый пример рекурсии, с которой сталкиваются большинство программистов - это нахождение факториала. Код может быть таким:

def factorial(n):

if n <= 1: return 1

else: return n * factorial(n - 1)

Как видно, мы записали инструкцию if else слегка необычным для питона способом, но это позволяется в данном случаее, ввиду того, что читабельность здесь не ухудшается, но не следует злоупотреблять таким стилем. И вообще, PEP8всех рассудит. :)

Теперь проясним несколько важных особенностей, о которых всегда нужно помнить при работе с рекурсиями.

    Существует ограничение на глубину рекурсии. По умолчанию оно равно 1000.

    Для того, чтобы изменить это ограничение нужно вызвать функцию sys.setrecursionlimit(), а для просмотра текущего лимита sys.getrecursionlimit().

    Не смотря на это - существует ограничение размером стека, который устанавливается операционной системой.

    Рекурсия в Python не может использоваться в функциях-генераторах и сопрограммах. Однако, можно это поведение исправить, но лучше не стоит.

    И последнее - применение декораторов к рекурсивным функциям может приводить к неожиданным результатам, поэтому будьте очень осторожны декорируя рекурсивные функции.

Также, всегда следует определять точки выхода из рекурсивных функций. Это как с циклами - бесконечный цикл может здорово «просадить» вашу операционную систему. И наконец, где лучше применять рекурсию, а где лучше воздержаться и обойтись, например циклами. Конечно, здесь многое зависит от задачи, но всегда следует помнить, что рекурсия в разы медленнее цикла. Так уж устроен питон, что вызов функции дорого вам обходится:) Вообще, в циклах не стоит вызывать функции, а уж рекурсивные функции и подавно.

Рекурсия хорошо подходит там, где производительность не слишком важна, а важнее читабельность и поддержка кода. К примеру, напишите две функции для обхода дерева каталогов, одну рекурсивную, другую с циклами.

Последнее обновление: 11.04.2018

Функции представляют блок кода, который выполняет определенную задачу и который можно повторно использовать в других частях программы. Формальное определение функции:

Def имя_функции ([параметры]): инструкции

Определение функции начинается с выражения def , которое состоит из имени функции, набора скобок с параметрами и двоеточия. Параметры в скобках необязательны. А со следующей строки идет блок инструкций, которые выполняет функция. Все инструкции функции имеют отступы от начала строки.

Например, определение простейшей функции:

Def say_hello(): print("Hello")

Функция называется say_hello . Она не имеет параметров и содержит одну единственную инструкцию, которая выводит на консоль строку "Hello".

Для вызова функции указывается имя функции, после которого в скобках идет передача значений для всех ее параметров. Например:

Def say_hello(): print("Hello") say_hello() say_hello() say_hello()

Здесь три раза подряд вызывается функция say_hello. В итоге мы получим следующий консольный вывод:

Hello Hello Hello

Теперь определим и используем функцию с параметрами:

Def say_hello(name): print("Hello,",name) say_hello("Tom") say_hello("Bob") say_hello("Alice")

Функция принимает параметр name, и при вызове функции мы можем передать вместо параметра какой-либо значение:

Hello, Tom Hello, Bob Hello, Alice

Значения по умолчанию

Некоторые параметры функции мы можем сделать необязательными, указав для них значения по умолчанию при определении функции. Например:

Def say_hello(name="Tom"): print("Hello,", name) say_hello() say_hello("Bob")

Здесь параметр name является необязательным. И если мы не передаем при вызове функции для него значение, то применяется значение по умолчанию, то есть строка "Tom".

Именованные параметры

При передаче значений функция сопоставляет их с параметрами в том порядке, в котором они передаются. Например, пусть есть следующая функция:

Def display_info(name, age): print("Name:", name, "\t", "Age:", age) display_info("Tom", 22)

При вызове функции первое значение "Tom" передается первому параметру - параметру name, второе значение - число 22 передается второму параметру - age. И так далее по порядку. Использование именованных параметров позволяет переопределить порядок передачи:

Def display_info(name, age): print("Name:", name, "\t", "Age:", age) display_info(age=22, name="Tom")

Именованные параметры предполагают указание имени параметра с присвоением ему значения при вызове функции.

Неопределенное количество параметров

С помощью символа звездочки можно определить неопределенное количество параметров:

Def sum(*params): result = 0 for n in params: result += n return result sumOfNumbers1 = sum(1, 2, 3, 4, 5) # 15 sumOfNumbers2 = sum(3, 4, 5, 6) # 18 print(sumOfNumbers1) print(sumOfNumbers2)

В данном случае функция sum принимает один параметр - *params , но звездочка перед названием параметра указывает, что фактически на место этого параметра мы можем передать неопределенное количество значений или набор значений. В самой функции с помощью цикла for можно пройтись по этому набору и произвести с переданными значениями различные действия. Например, в данном случае возвращается сумма чисел.

Возвращение результата

Функция может возвращать результат. Для этого в функции используется оператор return , после которого указывается возвращаемое значение:

Def exchange(usd_rate, money): result = round(money/usd_rate, 2) return result result1 = exchange(60, 30000) print(result1) result2 = exchange(56, 30000) print(result2) result3 = exchange(65, 30000) print(result3)

Поскольку функция возвращает значение, то мы можем присвоить это значение какой-либо переменной и затем использовать ее: result2 = exchange(56, 30000) .

В Python функция может возвращать сразу несколько значений:

Def create_default_user(): name = "Tom" age = 33 return name, age user_name, user_age = create_default_user() print("Name:", user_name, "\t Age:", user_age)

Здесь функция create_default_user возвращает два значения: name и age. При вызове функции эти значения по порядку присваиваются переменным user_name и user_age, и мы их можем использовать.

Функция main

В программе может быть определено множество функций. И чтобы всех их упорядочить, хорошей практикой считается добавление специальной функции main , в которой потом уже вызываются другие функции:

Def main(): say_hello("Tom") usd_rate = 56 money = 30000 result = exchange(usd_rate, money) print("К выдаче", result, "долларов") def say_hello(name): print("Hello,", name) def exchange(usd_rate, money): result = round(money/usd_rate, 2) return result # Вызов функции main main()

Функция в программировании представляет собой обособленный участок кода, который можно вызывать, обратившись к нему по имени, которым он был назван. При вызове происходит выполнение команд тела функции.

Функции можно сравнить с небольшими программками, которые сами по себе, т. е. автономно, не исполняются, а встраиваются в обычную программу. Нередко их так и называют – подпрограммы. Других ключевых отличий функций от программ нет. Функции также при необходимости могут получать и возвращать данные. Только обычно они их получают не с ввода (клавиатуры, файла и др.), а из вызывающей программы. Сюда же они возвращают результат своей работы.

Существует множество встроенных в язык программирования функций. С некоторыми такими в Python мы уже сталкивались. Это print(), input(), int(), float(), str(), type(). Код их тела нам не виден, он где-то "спрятан внутри языка". Нам же предоставляется только интерфейс – имя функции.

С другой стороны, программист всегда может определять свои функции. Их называют пользовательскими. В данном случае под "пользователем" понимают программиста, а не того, кто пользует программу. Разберемся, зачем нам эти функции, и как их создавать.

Предположим, надо три раза подряд запрашивать на ввод пару чисел и складывать их. С этой целью можно использовать цикл:

i = 0 while i < 3 : a = int (input () ) b = int (input () ) print (a+b) i += 1

Однако, что если перед каждым запросом чисел, надо выводить надпись, зачем они нужны, и каждый раз эта надпись разная. Мы не можем прервать цикл, а затем вернуться к тому же циклу обратно. Придется отказаться от него, и тогда получится длинный код, содержащий в разных местах одинаковые участки:

print () a = int (input () ) b = int (input () ) print ("Всего" , a+b, "шт." ) print () a = int (input () ) b = int (input () ) print ("Всего" , a+b, "шт." )

Пример исполнения программы:

Сколько бананов и ананасов для обезьян? 15 5 Всего 20 шт. Сколько жуков и червей для ежей? 50 12 Всего 62 шт. Сколько рыб и моллюсков для выдр? 16 8 Всего 24 шт.

Внедрение функций позволяет решить проблему дублирования кода в разных местах программы. Благодаря им можно исполнять один и тот же участок кода не сразу, а только тогда, когда он понадобится.

Определение функции. Оператор def

В языке программирования Python функции определяются с помощью оператора def. Рассмотрим код:

def countFood() : a = int (input () ) b = int (input () ) print ("Всего" , a+b, "шт." )

Это пример определения функции. Как и другие сложные инструкции вроде условного оператора и циклов функция состоит из заголовка и тела. Заголовок оканчивается двоеточием и переходом на новую строку. Тело имеет отступ.

Ключевое слово def сообщает интерпретатору, что перед ним определение функции. За def следует имя функции. Оно может быть любым, также как и всякий идентификатор, например, переменная. В программировании весьма желательно давать всему осмысленные имена. Так в данном случае функция названа "посчитатьЕду" в переводе на русский.

После имени функции ставятся скобки. В приведенном примере они пустые. Это значит, что функция не принимает никакие данные из вызывающей ее программы. Однако она могла бы их принимать, и тогда в скобках были бы указаны так называемые параметры.

После двоеточия следует тело, содержащее инструкции, которые выполняются при вызове функции. Следует различать определение функции и ее вызов. В программном коде они не рядом и не вместе. Можно определить функцию, но ни разу ее не вызвать. Нельзя вызвать функцию, которая не была определена. Определив функцию, но ни разу не вызвав ее, вы никогда не выполните ее тела.

Вызов функции

Рассмотрим полную версию программы с функцией:

def countFood() : a = int (input () ) b = int (input () ) print ("Всего" , a+b, "шт." ) print ("Сколько бананов и ананасов для обезьян?" ) countFood() print ("Сколько жуков и червей для ежей?" ) countFood() print ("Сколько рыб и моллюсков для выдр?" ) countFood()

После вывода на экран каждого информационного сообщения осуществляется вызов функции, который выглядит просто как упоминание ее имени со скобками. Поскольку в функцию мы ничего не передаем скобки опять же пустые. В приведенном коде функция вызывается три раза.

Когда функция вызывается, поток выполнения программы переходит к ее определению и начинает исполнять ее тело. После того, как тело функции исполнено, поток выполнения возвращается в основной код в то место, где функция вызывалась. Далее исполняется следующее за вызовом выражение.

В языке Python определение функции должно предшествовать ее вызовам. Это связано с тем, что интерпретатор читает код строка за строкой и о том, что находится ниже по течению, ему еще неизвестно. Поэтому если вызов функции предшествует ее определению, то возникает ошибка (выбрасывается исключение NameError):

print ("Сколько бананов и ананасов для обезьян?" ) countFood() print ("Сколько жуков и червей для ежей?" ) countFood() print ("Сколько рыб и моллюсков для выдр?" ) countFood() def countFood() : a = int (input () ) b = int (input () ) print ("Всего" , a+b, "шт." )

Результат:

Сколько бананов и ананасов для обезьян? Traceback (most recent call last ) : File "test.py" , line 2 , in < module> countFood() NameError: name "countFood" is not defined

Для многих компилируемых языков это не обязательное условие. Там можно определять и вызывать функцию в произвольных местах программы. Однако для удобочитаемости кода программисты даже в этом случае предпочитают соблюдать определенные правила.

Функции придают программе структуру

Польза функций не только в возможности многократного вызова одного и того же кода из разных мест программы. Не менее важно, что благодаря им программа обретает истинную структуру. Функции как бы разделяют ее на обособленные части, каждая из которых выполняет свою конкретную задачу.

Пусть надо написать программу, вычисляющую площади разных фигур. Пользователь указывает, площадь какой фигуры он хочет вычислить. После этого вводит исходные данные. Например, длину и ширину в случае прямоугольника. Чтобы разделить поток выполнения на несколько ветвей, следует использовать оператор if-elif-else:

figure = input () if figure == "1" : a = float (input ("Ширина: " ) ) b = float (input ("Высота: " ) ) print ("Площадь: %.2f" % (a*b) ) elif figure == "2" : a = float (input ("Основание: " ) ) h = float (input ("Высота: " ) ) print ("Площадь: %.2f" % (0.5 * a * h) ) elif figure == "3" : r = float (input ("Радиус: " ) ) print ("Площадь: %.2f" % (3.14 * r**2 ) ) else : print ("Ошибка ввода" )

Здесь нет никаких функций, и все прекрасно. Но напишем вариант с функциями:

def rectangle() : a = float (input ("Ширина: " ) ) b = float (input ("Высота: " ) ) print ("Площадь: %.2f" % (a*b) ) def triangle() : a = float (input ("Основание: " ) ) h = float (input ("Высота: " ) ) print ("Площадь: %.2f" % (0.5 * a * h) ) def circle() : r = float (input ("Радиус: " ) ) print ("Площадь: %.2f" % (3.14 * r**2 ) ) figure = input ("1-прямоугольник, 2-треугольник, 3-круг: " ) if figure == "1" : rectangle() elif figure == "2" : triangle() elif figure == "3" : circle() else : print ("Ошибка ввода" )

Он кажется сложнее, а каждая из трех функций вызывается всего один раз. Однако из общей логики программы как бы убраны и обособлены инструкции для нахождения площадей. Программа теперь состоит из отдельных "кирпичиков Лего". В основной ветке мы можем комбинировать их как угодно. Она играет роль управляющего механизма.

Если нам когда-нибудь захочется вычислять площадь треугольника по формуле Герона, а не через высоту, то не придется искать код во всей программе (представьте, что она состоит из тысяч строк кода как реальные программы). Мы пойдем к месту определения функций и изменим тело одной из них.

Если понадобиться использовать эти функции в какой-нибудь другой программе, то мы сможем импортировать их туда, сославшись на данный файл с кодом (как это делается в Python, будет рассмотрено позже).

Практическая работа

В программировании можно из одной функции вызывать другую. Для иллюстрации этой возможности напишите программу по следующему описанию.

Основная ветка программы, не считая заголовков функций, состоит из одной строки кода. Это вызов функции test(). В ней запрашивается на ввод целое число. Если оно положительное, то вызывается функция positive(), тело которой содержит команду вывода на экран слова "Положительное". Если число отрицательное, то вызывается функция negative(), ее тело содержит выражение вывода на экран слова "Отрицательное".