Питон для нормальных. Учебник Москва Базальт спо макс пресс 2018
Скачать 2.54 Mb.
|
f r o m math i m p o r t * d e f f u n c t i o n ( x ): f = x **2 -4 1 На самом деле никакого разделения на функции и процедуры в Python нет. Просто те функции, в которых возвращаемое значение не указано, возвращают специальное значение None . Более того, даже если функция какой-то результат возвращает, вы можете его проигно- рировать и использовать её как процедуру (в этом случае она, конечно, должна делать что-то полезное кроме вычисления возвращаемого значения). 96 Глава 4. Функции r e t u r n f d e f B i s e c t i o n (a , b , e ): w h i l e (b - a ) > e : c = ( a + b )/2 f_a = f u n c t i o n ( a ) f_b = f u n c t i o n ( b ) f_c = f u n c t i o n ( c ) i f f_a * f_c > 0: a = c e l s e : b = c r e t u r n (( a + b )/2) A = -10 B = 10 E = 10**( -15) p r i n t ( B i s e c t i o n (A , B , E )) Вывод программы: -2.0000000000000004 В Python результатом функции может быть только одно значение. Если необ- ходимо в качестве результата выдать значения сразу нескольких переменных, используют кортеж. Продемонстрируем это, дополнив программу вычислением количества шагов, за которые достигается значения корня с заданной точностью: f r o m math i m p o r t * d e f f u n c t i o n ( x ): f = x **2 -4 r e t u r n f d e f B i s e c t i o n (a , b , e ): n = 0 w h i l e (b - a ) > e : n = n + 1 c = ( a + b )/2 f_a = f u n c t i o n ( a ) f_b = f u n c t i o n ( b ) f_c = f u n c t i o n ( c ) i f f_a * f_c > 0: a = c e l s e : b = c r e t u r n (( a + b )/2 , n ) A = -10 B = 10 E = 10**( -15) 4.2. Параметры и аргументы функций 97 p r i n t ( B i s e c t i o n (A , B , E )) В качестве результата выдаётся кортеж из двух чисел: значения корня и коли- чества шагов, за которое был найден этот корень: (-2.0000000000000004, 55) Выражения тела функции выполняются лишь тогда, когда она вызывается в основной ветке программы. Так, например, если функция присутствует в исход- ном коде, но нигде не вызывается, то содержащиеся в ней инструкции не будут выполнены ни разу. 4.2 Параметры и аргументы функций Часто функция используется для обработки данных, полученных из внешней для нее среды (из основной ветки программы). Данные передаются функции при её вызове в скобках и называются аргументами. Однако чтобы функция могла «взять» передаваемые ей данные, необходимо при её создании описать парамет- ры (в скобках после имени функции), представляющие собой переменные. Пример: d e f summa (a , b ): c = a + b r e t u r n c num1 = i n t ( i n p u t ( ’Введите первое число: ’ )) num2 = i n t ( i n p u t ( ’Введите второе число: ’ )) summa ( num1 , num2 ) Здесь num1 и num2 суть аргументы функции, a и b суть параметры функции. В качестве аргументов могут выступать как числовые или строковые кон- станты вроде 12.3 , -9 или ’Hello’ , так и переменные или выражения, например, a+2*i 4.2.1 Обязательные и необязательные аргументы Аргументы функции могут быть обязательными или необязательными. Для всех необязательных аргументов необходимо указать значение по умолчанию. Рассмотрим функцию, возводящую один свой параметр в степень, заданную другим: d e f Degree (x , a ): f = x ** a r e t u r n f Если мы попробуем вызвать эту функцию с одним аргументом вместо двух, по- лучим ошибку: 98 Глава 4. Функции »> Degree(3) Traceback (most recent call last): File " ", line 1, in Degree(3) TypeError: Degree() missing 1 required positional argument: ’a’ Интерпретатор недоволен тем, что параметру с именем a не сопоставили ни од- ного значения. Если мы хотим, чтобы по умолчанию функция возводила x в квадрат, можно переопределить её следующим образом: d e f Degree (x , a =2): f = x ** a r e t u r n f Тогда наш вызов с одним аргументом станет корректным: >>> Degree (3) 9 При этом мы не теряем возможность использовать функцию Degree для воз- ведения в произвольную степень: >>> Degree (3 , 4) 81 В принципе, все параметры функции могут иметь значение по умолчанию, хотя часто это лишено смысла. Параметры, значение по умолчанию для кото- рых не задано, называются обязательными. Важно помнить, что обязательный параметр не может стоять после параметра, имеющего значение по умолчанию. Попытка написать функцию, не удовлетворяющую этому требованию, приведёт к синтаксической ошибке: >>> d e f Degree ( x =2 , a ): f = x ** a r e t u r n f S y n t a x E r r o r : non - default a r g u m e n t follows default a r g u m en t 4.2.2 Именованные аргументы Бывает, что у функции много или очень много параметров. Или вы забыли порядок, в котором они расположены, но помните их смысл. Тогда можно об- ратиться к функции, используя имена параметров как ключи. Пусть у нас есть следующая функция: d e f C l o t h i n g ( Dress , ColorDress , Shoes , C o l o r S h o e s ): S = ’Сегодня я надену ’ + C o l o r D r e s s + ’Ђ ’ \ + Dress + ’Ђи ’+ C o l o r S h o e s + ’Ђ ’ + Shoes r e t u r n S 4.2. Параметры и аргументы функций 99 Теперь вызовем нашу функцию, с аргументами не по порядку: p r i n t ( C l o t h i n g ( C o l o r D r e s s = ’красное’ , Dress = ’платье’ , C o l o r S h o e s = ’чёрные’ , Shoes = ’туфли’ )) Будет выведено: Сегодня я надену красное платье и чёрные туфли Как видим, результат получился верный, хотя аргументы перечислены не в том порядке, что при определении функции. Это происходит потому, что мы явно указали, какие параметры соответствуют каким аргументам. Следует отметить, что часто программисты на Python путают параметры со значением по умолчанию и вызов функции с именованными аргументами. Это происходит оттого, что синтаксически они плохо различимы. Однако важ- но знать, что наличие значения по умолчанию не обязывает вас использовать имя параметра при обращении к нему. Также и отсутствие значения по умолча- нию не означает, что к параметру нельзя обращаться по имени. Например, для описанной выше функции Degree все следующие вызовы будут корректными и приведут к одинаковому результату: >>> Degree (3) 9 >>> Degree (3 , 2) 9 >>> Degree (3 , a =2) 9 >>> Degree ( x =3 , a =2) 9 >>> Degree ( a =2 , x =3) 9 Чего нельзя делать, так это ставить обязательные аргументы после необяза- тельных, если имена параметров не указаны: >>> Degree ( a =2 , 3) S y n t a x E r r o r : non - keyword arg after keyword arg 4.2.3 Произвольное количество аргументов Иногда возникает ситуация, когда вы заранее не знаете, какое количество ар- гументов будет необходимо принять функции. Для такого случая есть специаль- ный синтаксис: все параметры обозначаются одним именем (обычно используется имя args) и перед ним ставится звёздочка *. Например: d e f unknown (* args ): f o r a r g u m e n t i n args : 100 Глава 4. Функции p r i n t ( a r g u m e n t ) unknown ( ’Что ’ , ’происходит’ , ’? ’) unknown ( ’Не знаю! ’) Вывод программы: Что происходит ? Не знаю! При этом тип значения args — кортеж, содержащий все переданные аргументы по порядку. 4.3 Локальные и глобальные переменные Если записать в IDLE приведённую ниже функцию, и затем попробовать вы- вести значения переменных, то обнаружится, что некоторые из них почему-то не существуют: >>> d e f mathem (a , b ): a = a /2 b = b +10 p r i n t ( a + b ) >>> num1 = 100 >>> num2 = 12 >>> mathem ( num1 , num2 ) 72.0 >>> num1 > > >100 >>> num2 12 >>> a T r a c e b a c k ( most recent call last ): File " < pyshell #10 > " , line 1 , i n < module > a N a m e E r r o r : name ’a ’ i s n o t defined >>> b T r a c e b a c k ( most recent call last ): File " < pyshell #11 > " , line 1 , i n < module > b N a m e E r r o r : name ’b ’ i s n o t defined >>> Переменные num1 и num2 не изменили своих первоначальных значений. Дело в том, что в функцию передаются копии значений. Прежние значения из основной ветки программы остались связанны с их переменными. 4.3. Локальные и глобальные переменные 101 А вот переменных a и b, оказывается, нет и в помине (ошибка name ’b’ is not defined переводится как "переменная b не определена" ). Эти переменные су- ществуют лишь в момент выполнения функции и называются локальными. В противовес им, переменные num1 и num2 видны не только во внешней ветке, но и внутри функции: >>> d e f mathem2 (): p r i n t ( num1 + num2 ) >>> mathem2 () 112 >>> Переменные, определённые в основной ветке программы, являются глобаль- ными. Итак, в Python две базовых области видимости переменных: 1. глобальные переменные, 2. локальные переменные. Переменные, объявленные внутри тела функции, имеют локальную область видимости, те, что объявлены вне какой-либо функции, имеют глобальную об- ласть видимости. Это означает, что доступ к локальным переменным имеют только те функции, в которых они были объявлены, в то время как доступ к глобальным переменным можно получить по всей программе в любой функции. Например: Place = ’Солнечная система’ # Глобальная переменная d e f g l o b a l _ P o s i t i o n (): p r i n t ( Place ) d e f l o c a l _ P o s i t i o n (): Place = ’Земля’ # Локальная переменная p r i n t ( Place ) S = i n p u t () i f S == ’система’: g l o b a l _ P o s i t i o n () e l s e : l o c a l _ P o s i t i o n () Вывод программы при двух последовательных запусках: система Солнечная система >>> = = = = = = = = = = = RESTART : / home / paelius / Python /1. py = = = = = = == = = = планета Земля 102 Глава 4. Функции Важно помнить, что для того чтобы получить доступ к глобальной перемен- ной на чтение, достаточно лишь указать её имя. Однако если перед нами стоит задача изменить глобальную переменную внутри функции, необходимо исполь- зовать ключевое слово global. Например: Number = 10 d e f change (): g l o b a l Number Number = 20 p r i n t ( Number ) change () p r i n t ( Number ) Вывод программы: 10 20 Если забыть написать строчку global Number, то интерпретатор выдаст следу- ющее: 10 10 4.4 Программирование сверху вниз Вряд ли стоило бы уделять много внимания функциям, если бы за ними не скрывались важные и основополагающие идеи. В действительности, функции оказывают решающее влияние на стиль и качество работы программиста. Функ- ция — это не только способ сокращения текста, но что более важно, средство разложения программы на логически связанные, замкнутые компоненты, опре- деляющие её структуру. Представьте себе программу, содержащую, например, 1000 строк кода (это ещё очень маленькая программа). Обозреть такое количество строк и понять, что делает программа, было бы практически невозможно без функций. Большие программы строятся методом последовательных уточнений. На пер- вом этапе внимание обращено на глобальные проблемы, и в первом эскизном про- екте упускаются из виду многие детали. По мере продвижения процесса создания программы глобальные задачи разбиваются на некоторое число подзадач. Те, в свою очередь, на более мелкие подзадачи и т.д., пока решать каждую подзадачу не станет достаточно просто. Такая декомпозиция и одновременная детализация программы называется нисходящим методом программирования или програм- мированием сверху вниз. Концепция функций позволяет выделить отдельную подзадачу как отдель- ную подпрограмму. Тогда на каждом этапе можно придумать имена функций для подзадач, вписывать в раздел описаний их заголовки и, ещё не добавляя к 4.5. Рекурсивный вызов функции 103 ним тело функции, уже использовать их вызовы для создания каркаса програм- мы так, будто они уже написаны. Например, на численных методах студенты решают задачу сравнения двух методов поиска корня уравнения: уже расписанного выше метода деления отрез- ка пополам и метода Ньютона. Необходимо понять, какой из методов быстрее сходится к корню с заданной точностью. Концепция программирования «сверху вниз» предусматривает, что вначале студенты напишут «скелет» программы, а уже потом начнут разбираться, как какая функция функционирует в отдельности: d e f F u n c t i o n ( X ) #Наше нелинейное уравнение d e f Newton (a , b , E ) # Метод Ньютона d e f B i s e c t i o n (a , b , E ) # Метод деления отрезка пополам A = ? B = ? E = ? B i s e c t i o n (A , B , E ) #Вызов функции метода деления отрезка пополам# Newton (A , B , E ) #Вызов функции метода Ньютона# Какие именно инструкции будут выполняться функциями Bisection и Newton пока не сказано, но уже известно, что они принимают на вход и что долж- ны возвращать. Это следующий этап написания программы. Потом нам известно, что наше нелинейное уравнение, корень которого необходимо найти, желатель- но оформить в виде отдельной функции. Так появляется функция Function, которая будет вызываться внутри функций Bisection и Newton. Когда каркас создан, остаётся только написать тела функций. Преимущество такого подхода в том, что, создавая тело каждой из функций, можно не думать об остальных функциях, сосредоточившись на одной подзадаче. Кроме того, когда каждая из функций имеет понятный смысл, гораздо легче, взглянув на програм- му, понять, что она делает. Это в свою очередь позволит: (а) допускать меньше логических ошибок и (б) организовать совместную работу нескольких програм- мистов над большой программой. Важной идеей при таком подходе становится использование локальных пере- менных. В глобальную переменную можно было бы записать результат работы функции, однако это будет стилистической ошибкой. Весь обмен информацией функция должна вести исключительно через параметры. Такой подход позволя- ет сделать решение каждой подзадачи максимально независимым от остальных подзадач, упрощает и упорядочивает структуру программы. 4.5 Рекурсивный вызов функции Рекурсия в программировании — это вызов функции из неё же самой непо- средственно (простая рекурсия) или через другие функции (сложная или кос- венная рекурсия), например, функция A вызывает функцию B, а функция B — 104 Глава 4. Функции функцию A. Количество вложенных вызовов функции или процедуры называется глубиной рекурсии. Рекурсивная программа позволяет описать повторяющееся или даже потенциально бесконечное вычисление, причём без явных повторений частей программы и использования циклов. Принцип работы рекурсивной функции удобнее всего объяснять на приме- ре матрёшек (рис. 4.2). Пусть есть набор нераскрашенных матрёшек. Нужно узнать, в какие цвета покрасить самую большую матрёшку. При этом раскраше- на только самая маленькая матрёшка, которая не раскрывается. Необходимо по- следовательно раскрывать матрёшки, пока не дойдём до той, которую раскрыть не получается — это раскрашенная матрёшка. Далее можно раскрашивать каж- дую следующую матрёшку по возрастанию, держа перед собою предыдущую как пример и затем вкладывать все меньшие, раскрашенные ранее, во вновь раскра- шенную. От матрёшек можно перейти к классическому примеру рекурсии, которым может послужить функция вычисления факториала числа: d e f fact ( num ): i f num == 0: r e t u r n 1 e l s e : r e t u r n num * fact ( num - 1) n = i n t ( i n p u t ( ’Введите число ’ )) p r i n t ( fact ( n )) Структурно рекурсивная функция на верхнем уровне всегда представляет собой команду ветвления (выбор одной из двух или более альтернатив в зависи- мости от условия (условий), которое в данном случае уместно назвать «условием прекращения рекурсии», имеющей две или более альтернативные ветви, из ко- торых хотя бы одна является рекурсивной и хотя бы одна — терминальной. В вышеприведённых примерах с матрёшками и с вычислением факториала условия «если матрёшка не раскрывается» и if num==0 являются командами ветвления, ветвь return 1 (или самая маленькая раскрашенная матрёшка) яв- ляется терминальной, а ветвь return num*fact(num-1) (постепенное раскраши- вание и закрывание матрёшек) — рекурсивной. Рекурсивная ветвь выполняется, когда условие прекращения рекурсии лож- но, и содержит хотя бы один рекурсивный вызов — прямой или опосредованный вызов функцией самой себя. Терминальная ветвь выполняется, когда условие прекращения рекурсии истинно; она возвращает некоторое значение, не выпол- няя рекурсивного вызова. Правильно написанная рекурсивная функция должна гарантировать, что через конечное число рекурсивных вызовов будет достигнуто выполнение условия прекращения рекурсии, в результате чего цепочка последо- вательных рекурсивных вызовов прервётся и выполнится возврат. Помимо функций, выполняющих один рекурсивный вызов в каждой рекур- сивной ветви, бывают случаи «параллельной рекурсии», когда на одной рекур- сивной ветви делается два или более рекурсивных вызова. Параллельная ре- 4.5. Рекурсивный вызов функции 105 Рис. 4.2. Наглядное представление принципа работы рекурсивной функции. 106 Глава 4. Функции курсия типична при обработке сложных структур данных, таких как деревья. Простейший пример параллельно-рекурсивной функции – вычисление ряда Фи- боначчи, где для получения значения n-го члена необходимо вычислить (n − 1)-й и (n − 2)-й: d e f fib ( n ): i f n <3: r e t u r n 1 r e t u r n fib (n -1) + fib (n -2) n = i n t ( i n p u t ( ’Введите число: ’ )) p r i n t ( fib (10)) Реализация рекурсивных вызовов функций в практически применяемых язы- ках и средах программирования, как правило, опирается на механизм стека вы- зовов — адрес возврата и локальные переменные функции записываются в стек, |