Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 1606.pdf
Скачиваний:
18
Добавлен:
30.04.2022
Размер:
1.48 Mб
Скачать

– с явным указанием имени аргументов, при этом порядок перечисления аргументов не имеет значения (такой способ называется пере дачей аргументов по ключу или по имени, а сами аргументы иногда называют именованными).

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

– позиционным способом, то в команде вызова сначала указываются позиционные аргументы, а затем те, что передаются по ключу. Стоит также особо обратить внимание, что способ описания аргументов (при определении функции) не зависит tого, как предполагается передавать аргументы при вызове функции (по ключу или позиционно).

Предположим, нам нужно описать функцию такую, чтобы ее можно было вызывать с разным количеством аргументов. Здесь важно, что количество аргументов не ограничено – мы наперед не знаем, сколько в принципе будет передаваться аргументов функции. В этом случае мы описываем функцию с одним аргументом, но перед этим аргументом ставим звездочку *. Такой аргумент отождествляется со списком, элементы которого формируются реальными аргументами, что переданы функции при вызове. Другими словами, наш "звездный" аргумент в теле функции обрабатывается как спи сок. Но при вызове функции аргументы передаются, как обычно. Например, функция может быть описана так:

def get_sum(*nums): s=0

for a in nums: s+=a

return s

Данная функция в качестве результата будет возвращать сумму чисел, переданных аргументами функции. Скажем, значением выражения get_sum (1, 3, 5, 2) будет число 11, а значение выражения get_sum (- 2, 4) - Число 2.

3.3. Рекурсия

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

#Функция для вычислений чисел Фибоначчи.

#При описании функции использована рекурсия def Fib (n):

#Первое и второе число

#в последовательности равны 1

23

if n==l or n==2: return 1

# Числа в последовательности равно сумме двух предыдущих else:

return Fib (n- l) +Fib (n - 2)

#Проверяем работу функции print ("Чиcлa Фибоначчи: ")

#Вычисляем 1 5 первых чисел Фибоначчи for i in range ( l , 1 6 ) :

print (Fib ( i ) , end=" " )

Врезультате выполнения программы в ряд через пробел отображается 15 чисел из последовательности Фибоначчи:

Числа Фибоначчи:

1 1 2 3 5 8 13 21 34 55 89 144 233 377 610

Функция для вычисления чисел Фибоначчи в нашем исполнении называется Fib() и у нее один аргумент (обозначен как n) – это порядковый номер числа в последовательности . По этому номеру необходимо вычислить число. Процесс вычислений мы реализуем с помощью условного оператора, в котором проверяем значение аргумента функции. Точнее, проверяется условие n==l or n==2. Условие истинно, если аргумент n равен 1 или 2, то есть если речь идет о первом или втором числе в последовательности. В этом случае число Фибоначчи равно 1. Поэтому если условие истинно, выполняется инструкция return 1. То есть если функции Fib() значение аргумента равно 1 или 2, функцией возвращается значение 1.

Пока все просто. Ситуация усложняется, если условие n==l o r n==2 ложно. Ложно условие, если индекс у числа (его порядковый номер в последовательности) больше чем 2 (экзотические варианты с отрицательными и нецелыми индексами мы не рассматриваем). И здесь мы вспоминаем правило вычисления чисел в последовательности Фибоначчи: каждое число (кроме первых двух) – это сумма двух предыдущих. Далее, если число Фибоначчи с порядковым номером n возвращается в результате вызова функции командой Fib(n), то два предыдущих числа - это Fib(n-1) и Fib(n- 2). Поэтому в теле функции (с аргументом n) в условном операторе в еlsе -блоке выполняется команда return Fib(n-1) + F ib (n - 2). Собственно, на этом все. У нас есть программный код функции, и этот программный код рабочий.

Для проверки работы функции Fib () мы с ее помощью вычисляем и выводим в одну строку 15 первых чисел из последовательности Фибоначчи.

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

Рассмотрим еще один пример рекурсии. На этот раз перепишем пример из листинга, в котором, напомним, представлена программа для решения

24

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

#Описание функции для решения уравнения.

#Используем рекурсию def solve (f , xO , n ) :

#Начальное приближение if n==O : return хО

#Рекурсивное соотношение else :

return solve ( f , f ( xO ) , n- 1 )

#Функция , определяющая уравнение de f eqn ( х ) :

#Значение функции return (x**2+5) / 6

#Решаем уравнение x=s 9lve ( eqn, 0, 10 )

#Отображаем результат

print (" Peшeниe уравнения : х = " , х )

Мы немного изменили программный код: частью переписали, частью – упростили. Функция solve() предназначена для решения уравнения, определяемого первым аргументом (ссылка на функцию уравнения f) с начальным приближением, определяемым вторым аргументом (обозначен как х0) по количеству итераций, определяемому третьим аргументом (обозначен как n).

Основу программного кода функции составляет условный оператор, в котором проверяется условие n == 0. Что означает истинность этого условия? Истинность этого условия означает, что мы вычисляем нулевое приближение для корня уравнения. Но нулевое приближение определяется вторым аргументом х0 функции solve(). Поэтому нет ничего удивительного в том, что при истинном условии n==0 командой return х0 значение х0 возвращается как результат функции solve() . Но если условие n==0 ложно, в качестве результата возвращается выражение sо1ve(f , f( х0) , n - 1) , в котором рекурсивно

вызывается функция solve ( ).

 

 

=

 

+56

Результат работы созданной нами функции solve ( ) проверяем для

решения уравнения

 

2

 

. С этой целью в программе описана функция

eqn(). В итоге получаем такой результат:

Решение уравнения: х = 0 . 9999925289581152

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

25

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

3.4. Лямбда-функции

Мы уже знаем, что имя функции можно присвоить в качестве значения переменной, после чего через эту переменную допустимо ссылаться на функцию. Но в языке Python есть и другая "крайность": мы можем создать функцию, но имя ей не присваивать вовсе. Такие функции называются анонимными функциями или лямбда-функциями. Зачем подобные функции нужны – это вопрос отдельный. Можно привести как минимум несколько примеров, когда использование лямбда-функций представляется оправданным:

передача лямбда-функции аргументом другой функции;

возвращение функцией в качестве результата другой функции;

однократное использование функции.

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

lambda аргументы: результат

У этой конструкции есть результат: ссылка на объект лямбда-функции. Поэтому в принципе lаmbdа-конструкцию можно присвоить в качестве значения переменной, тем самым определив, в общем-то, обычную функцию. Небольшой пример, иллюстрирующий некоторые аспекты объявления и использования лямбда-функций, приведен в листинге:

#Функция для отображения значения

#другой функции

def find_value (f, x):

print ("x =" , х , " - > f (x ) =" , f ( x ) )

#Переменной присваивается

#ссылка на лямбда-функцию my_func=lambda 0 x: l / (l +x* *2)

# Проверяем результат find_value (my_func, 2.0)

#Аргумента функции передана

#лямбда-функция

find_value (lambda х: х* (1 - х) , 0 . 5)

#Использование лямбда-функции

# в выражении

26

z=l+ (lamЬda х, у: х * у-х* *2) (2, 3 } * * 2

#Проверяем значение переменных print (" z =" , z)

В программном коде объявляется функция find value ( ) с двумя аргументами. Это самая обычная функция. Мы собираемся ее использовать как вспомогательную для иллюстрации к использованию лямбда-функций. Первый аргумент функции f, как предполагается, является ссылкой на функцию, а х – предполагаемый аргумент для этой функции. В теле функции find value ( ) командой print(" х = " , х , " -> f ( х ) =" , f ( х ) ) отображается сообщение, в котором, кроме прочего, использована инструкция f ( х ) вызова функции, на которую ссылается переменная f, с аргументом х.

Далее приведено несколько примеров описания и использования лямбда функций. Так, в команде my func=lambda х: 1 / (1 +х * * 2) есть описание лямбда-функции, а ссылка на эту функцию присваивается переменной my_func. Собственно, функция описывается инструкцией lambda х: 1 / (1 +х * * 2), в которой после ключевого слова lambda указано формальное название х для аргумента функции, а выражение 1 / (1+х**2) означает, что при аргументе функции х ее результатом будет значение 1/(1+х**2).

Чтобы проверить "работоспособность" созданной нами функции, используем команду find_value(my_func, 2.0). Б этой команде первым аргументом функции find_value() передается имя переменной my_func, которая содержит ссылку на лямбда-функцию. Второй аргумент 2.0 функции

find_value( ) – это значение аргумента для функции, на которую ссылается переменная my_func. В результате в консольное окно выводится со общение со значением my_func (2.0). Результатом является значение 0.2 (легко проверить, что f(2) = 1 +1 22 = 51 = 0.2). Еще один вариант, который мы рассматриваем - передача лямбда-функции в качестве аргумента функции. Соответствующая команда выглядит как find value (lambda х : х * ( 1 -х ) , 0.5 ) . Здесь первым аргументом функции find_value( ) передается не переменная со ссылкой на лямбда функцию, как в предыдущем случае, а сама лямбдафункция. Первый аргумент выглядит так: lambda х : х * ( 1 -х ). То есть аргументу функции x в соответствие ставится выражение х * (1-х) (это означает, что мы имеем дело с функцией f(x) = х · (1 - х)). Второй аргумент функции find_value( ) - числовое значение 0.5. Это значение играет роль аргумента для функции, переданной первым аргументом функции find _value( ). И хотя первым аргументом передана не переменная, а анонимная функция, суть дела не меняется: вычисляется значение f (0.5) = 0.5. (1 - 0.5) = 0.25 , то есть вычисленное значение равно 0.25.

Третий пример использования лямбда-функции: использование такой функции в выражении. Примером служит команда z = l + ( lambda х , у : х * уx* * 2 ) ( 2 , 3 ) * * 2 , в результате выполнения которой переменная z получает значение 5 . Значение выражения 1 + ( lambda х , у : х * у- х * * 2 ) ( 2 , 3 ) * * 2 вычисляется так: к единице прибавляется значение выражения ) lambda х , у :

27

х * у-х * * 2 ) ( 2 , 3 ) ' * * 2. Данное выражение – это квадрат А это выражение, значения выражения(1amЬdaх,у:х*у-х**2)(2,3) в свою очередь, есть не что

иное, как результат действия лямбда-функции (lambda

х, у :х * у-х * *2) на

аргументы (2,3 ). Инструкцией lambda х , у : х * у-х

* * 2 определяется

Лямбда-функция двух аргументов ( х и у), а результатом является значение х * у-х * * 2 , вычисляемое на основе значений аргументов функции. То есть здесь имеем дело с функцией f(x, y) =· ху - х2• Если вычислять значение этой функции для аргументов х = 2, у = 3 получим f (2,З) = 2 · З - 2 2 = 6 - 4 = 2 . Следовательно, значением выражения ( 1 ambda х , у : х * у-х * * 2 ) ( 2 , 3 ) является 2 , значением выражения ( 1 ambda х , у : х * у-х * * 2 ) _( 2 , 3 ) * * 2 является 4, а значение выражения 1+ ( lambda х·, у : х * у-х* * 2 ) ( 2 , 3 ) * * 2 , таким образом, 5.

В результате выполнения программы получаем такие сообщения в окне вывода:

х= 2 . 0 - > f ( х ) = 0 . 2

х=0. 5 - > f ( х ) = 0.25 z =5

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

Стоит отметить концептуально важный момент. Когда мы вызываем функцию (например, командой вида функция (аргументы), то сама инструкция вызова функции тоже является функцией. То есть выражение функция (аргументы) можно интерпретировать как имя функции (если точнее, то ссылку на функцию). Это ссылка именно на ту функцию, которая возвращается как результат вызываемой функции. Поэтому если после инструкции функция (аргументы) в круглых скобках указать аргументы, то получим вызов функциирезультата. Другими словами, при вызове функции-результата имеем дело с выражением вида функция(аргументы)(аргументы). Можно поступить иначе: присвоить некоторой переменной значение выражения функция(аргументы). После выполнения команды переменная = функция (аргументы) с переменной можно обращаться как с именем функции.

#Функция в качестве результата

#возвращает функцию

def my_pow ( n ) : return lambda х : x * * n

# Проверяем результат

for n in range ( l , 4 ) # Внешний цикл

for х in range ( l , 1 1 ) : # Внутренний цикл

# Выводим результат· вызова функции print (my_pow ( n ) ( х ) , end=" " )

print ( ) # Переходим к новой строке

28