Математический анализ. 3е издание
Скачать 4.86 Mb.
|
436 Глава 16. Области видимости и аргументы В заключение В этой главе мы изучили две ключевые концепции, имеющие отноше ние к функциям: области видимости (как выполняется поиск перемен ных при обращениях к ним) и аргументы (как объекты передаются в функции). Здесь мы узнали, что переменные считаются локальными для определений функций, где выполняется присваивание значений этим переменным при условии, что они не объявлены как глобальные. Кроме того, мы узнали, что аргументы передаются функции через опе рацию присваивания, т. е. в виде ссылок на объекты, которые в дейст вительности являются указателями. Мы также познакомились с дополнительными особенностями облас тей видимости и аргументов, например с областями видимости вло женных функций и аргументами, которые передаются по ключу. На конец мы познакомились с некоторыми рекомендациями по проекти рованию приложений (стремиться минимизировать количество гло бальных переменных и избегать изменений переменных в соседних файлах) и увидели, что изменяемые объекты в аргументах проявляют то же самое поведение, как и другие разделяемые ссылки на объекты, – если функции явно не передается копия объекта, воздействие непо Придется держать в уме: аргументы, передаваемые по ключу Аргументы, которые передаются по ключу, играют важную роль в библиотеке Tkinter, которая фактически стала стандартным средством для разработки графического интерфейса в языке Py thon. Мы познакомимся с Tkinter далее в этой книге, но в каче стве предварительного знакомства замечу, что при использова нии этой библиотеки для установки значений параметров ком понентов графического интерфейса используются аргументы, передаваемые по ключу. Например, следующий вызов: from Tkinter import * widget = Button(text="Press me", command=someFunction) создает новую кнопку и определяет текст на кнопке и функцию обратного вызова, с помощью ключевых аргументов text и com mand . Так как графические компоненты могут иметь большое чис ло параметров, аргументы, передаваемые по ключу, позволяют указывать только необходимые вам параметры. В противном случае пришлось бы перечислять все возможные параметры в со ответствии с их позициями или надеяться, что аргументы со зна чениями по умолчанию будут правильно интерпретироваться во всех возможных ситуациях. Закрепление пройденного 437 средственно на изменяемый объект может отразиться на вызывающей программе. Следующая глава завершает тему функций исследованием более сложных концепций, связанных с функциями: lambdaвыражений, ге нераторов, итераторов, функциональных инструментов, таких как map , и т. д. Многие из этих концепций исходят из того, что функции в языке Python являются обычными объектами и потому поддержи вают дополнительные, очень гибкие режимы работы. Однако, прежде чем углубиться в эти темы, изучите контрольные вопросы к этой гла ве, чтобы закрепить знания, полученные здесь. Закрепление пройденного Контрольные вопросы 1. Что выведет следующий фрагмент и почему? >>> X = 'Spam' >>> def func(): ... print X >>> func() 2. Что выведет следующий фрагмент и почему? >>> X = 'Spam' >>> def func(): ... X = 'NI!' >>> func() >>> print X 3. Что выведет следующий фрагмент и почему? >>> X = 'Spam' >>> def func(): ... X = 'NI' ... print X >>> func() >>> print X 4. Что выведет следующий фрагмент на этот раз и почему? >>> X = 'Spam' >>> def func(): ... global X ... X = 'NI' >>> func() >>> print X 438 Глава 16. Области видимости и аргументы 5. Что можно сказать об этом фрагменте – что он выведет и почему? >>> X = 'Spam' >>> def func(): ... X = 'NI' ... def nested(): ... print X ... nested() >>> func() >>> X 6. И в последний раз: что выведет следующий фрагмент и почему? >>> def func(a, b, c=3, d=4): print a, b, c, d >>> func(1, *(5,6)) 7. Назовите тричетыре способа в языке Python сохранять информа цию о состоянии в функциях. 8. Назовите три способа, которые могут использоваться для передачи результатов из функции в вызывающую программу. Ответы 1. В данном случае будет выведена строка 'Spam', потому что функция обращается к глобальной переменной в объемлющем модуле (если внутри функции переменной не присваивается значение, она ин терпретируется как глобальная). 2. В данном случае снова будет выведена строка 'Spam', потому что опе рация присваивания внутри функции создает локальную перемен ную и тем самым скрывает глобальную переменную с тем же име нем. Инструкция print находит неизмененную переменную в гло бальной области видимости. 3. Будет выведена последовательность символов 'Ni' в одной строке и 'Spam' – в другой, потому что внутри функции инструкция print найдет локальную переменную, а за ее пределами – глобальную. 4. На этот раз будет выведена строка 'Ni', потому что объявление glo bal предписывает выполнять присваивание внутри функции пере менной, находящейся в глобальной области видимости объемлю щего модуля. 5. В этом случае снова будет выведена последовательность символов 'Ni' в одной строке и 'Spam' – в другой, потому что инструкция print во вложенной функции отыщет имя в локальной области видимо сти объемлющей функции, а инструкция print в конце фрагмента отыщет имя в глобальной области видимости. 6. Здесь будет выведено "1 5 6 4": 1 соответствует аргументу в первой позиции, 5 и 6 соответствуют аргументам b и c в соответствии с фор Закрепление пройденного 439 мой *name (значение 6 переопределяет значение по умолчанию аргу мента c) и d получит значение по умолчанию 4, потому что четвер тый аргумент в вызове функции отсутствует. 7. Так как значения локальных переменных исчезают, когда функ ция возвращает управление, то информацию о состоянии в языке Python можно сохранять в глобальных переменных, для вложен ных функций – в области видимости объемлющих функций, а так же посредством аргументов со значениями по умолчанию. Альтер нативный способ заключается в использовании классов и приемов ООП, который обеспечивает лучшую поддержку возможности со хранения информации о состоянии, чем любой из трех предыду щих приемов, потому что этот способ делает сохранение явным, по зволяя выполнять присваивание значений атрибутам. 8. Функции могут возвращать результаты с помощью инструкции re turn , воздействуя на изменяемые объекты, передаваемые в аргу ментах, а также изменением глобальных переменных. Вообще гло бальные переменные использовать для этих целей не рекомендует ся (за исключением редких случаев, таких как многопоточные про граммы), потому что это осложняет понимание и использование программного кода. Наилучшим способом является инструкция re turn , хотя воздействие на изменяемые объекты – тоже неплохой ва риант при условии, что он предусмотрен заранее. Кроме того, функ ции могут выполнять обмен информацией через такие системные устройства, как файлы и сокеты, но это уже выходит за рамки дан ной главы. 17 Расширенные возможности функций В этой главе будут представлены дополнительные расширенные воз можности, имеющие отношение к функциям: lambdaвыражения, сред ства функционального программирования, такие как функция map и генераторы списков, функциигенераторы и выражениягенераторы и многие другие. Отчасти искусство использования функций лежит в области интерфейсов между ними, поэтому здесь мы также исследу ем некоторые общие принципы проектирования функций. Так как это последняя глава в четвертой части, ее завершает раздел с описанием типичных ошибок и упражнения, которые помогут вам приступить к использованию отраженных здесь идей. Анонимные функции: lambda Вы уже знаете, как писать свои собственные обычные функции на языке Python. В следующих разделах будет описано несколько более сложных концепций, имеющих отношение к функциям. Большинство из них не являются обязательными, но при грамотном использовании они могут упростить задачи программирования. Помимо инструкции def в языке Python имеется возможность создавать объекты функций в форме выражений. Изза сходства с аналогичной возможностью в языке LISP она получила название lambda. 1 Подобно инструкции def это выражение создает функцию, которая будет вызы ваться позднее, но в отличие от инструкции def, выражение возвращает 1 Название «lambda» отпугивает многих программистов, хотя в нем нет ни чего страшного. Это название происходит из языка программирования LISP, в котором это название было заимствовано из лямбдаисчисления – разновидности символической логики. Однако в языке Python это просто ключевое слово, которое вводит выражение синтаксически. Анонимные функции: lambda 441 функцию, а не связывает ее с именем. Именно поэтому lambdaвыраже ния иногда называют анонимными (т. е. безымянными) функциями. На практике они часто используются как способ получить встроенную функцию или отложить выполнение фрагмента программного кода. lambdaвыражения В общем виде lambda – это ключевое слово, за которым следуют один или более аргументов (точно так же, как список аргументов в круглых скобках в заголовке инструкции def), и далее, вслед за двоеточием, на ходится выражение: lambda argument1, argument2,... argumentN : выражение, использующее аргументы В качестве результата lambdaвыражения возвращают точно такие же объекты функций, которые создаются инструкцией def, но здесь есть несколько различий, которые делают lambdaвыражения удобными в не которых специализированных случаях: • lambda – это выражение, а не инструкция. По этой причине ключе вое слово lambda может появляться там, где синтаксис языка Python не позволяет использовать инструкцию def, например внутри лите ралов или в вызовах функций. Кроме того, lambdaвыражение воз вращает значение (новую функцию), которое при желании можно присвоить переменной, в противовес инструкции def, которая все гда связывает функцию с именем в заголовке, а не возвращает ее в виде результата. • Тело lambda – это не блок инструкций, а выражение. Тело lambdaвы ражения сродни тому, что вы помещаете в инструкцию return внут ри определения def, – вы просто вводите результат в виде выраже ния вместо его явного возврата. Вследствие этого ограничения lamb da выражения менее универсальны, чем инструкция def, – в теле lambda выражения может быть реализована только логика, не ис пользующая такие инструкции, как if. Такая реализация преду смотрена заранее – она ограничивает возможность создания боль шого числа уровней вложенности программ; lambdaвыражения предназначены для создания простых функций, а инструкции def – для решения более сложных задач. Если отвлечься от этих различий, def и lambda выполняют одну и ту же работу. Например, мы уже видели, как создаются функции с помощью инструкции def: >>> def func(x, y, z): return x + y + z >>> func(2, 3, 4) 9 Но того же эффекта можно достигнуть с помощью lambdaвыражения, явно присвоив результат имени, которое позднее будет использоваться для вызова функции: 442 Глава 17. Расширенные возможности функций >>> f = lambda x, y, z: x + y + z >>> f(2, 3, 4) 9 Здесь имени f присваивается объект функции, созданный lambdaвыра жением, – инструкция def работает точно так же, но присваивание вы полняет автоматически. В lambdaвыражениях точно так же можно использовать аргументы со значениями по умолчанию: >>> x = (lambda a="fee", b="fie", c="foe": a + b + c) >>> x("wee") 'weefiefoe' Для lambdaвыражений используются те же самые правила поиска пере менных в областях видимости, что и для вложенных инструкций def. lambda выражения создают локальную область видимости, как и вло женные инструкции def, и автоматически получают доступ к именам в объемлющих функциях, в модуле и во встроенной области видимо сти (в соответствии с правилом LEGB): >>> def knights(): ... title = 'Sir' ... action = (lambda x: title + ' ' + x) # Заголовок в объемлющей def ... return action # Возвращает функцию >>> act = knights() >>> act('robin') 'Sir robin' В этом примере до версии Python 2.2 значение для имени title переда валось бы в виде значения по умолчанию – если вы забыли, почему, вернитесь к главе 16, где рассматривались области видимости. Зачем использовать lambdaвыражения? Вообще говоря, lambdaвыражения очень удобны для создания очень маленьких функций, к тому же они позволяют встраивать определе ния функций в программный код, который их использует. Они не яв ляются предметом первой необходимости (вы всегда сможете вместо них использовать инструкции def), но они позволяют упростить сцена рии, где требуется внедрять небольшие фрагменты программного кода. Например, позднее мы увидим, что функции обратного вызова часто реализуются в виде lambdaвыражений, встроенных непосредственно в список аргументов, вместо инструкций def гдето в другом месте в мо дуле и передаваемых по имени (примеры вы найдете во врезке «Придет ся держать в уме: функции обратного вызова» ниже в этой главе). lambda выражения также часто используются для создания таблиц пе+ реходов , которые представляют собой списки или словари действий, выполняемых по требованию. Например: Анонимные функции: lambda 443 L = [(lambda x: x**2), (lambda x: x**3), (lambda x: x**4)] for f in L: print f(2) # Выведет 4, 8, 16 print L[0](3) # Выведет 9 lambda выражения наиболее полезны в качестве сокращенного вариан та инструкции def, когда необходимо вставить маленькие фрагменты исполняемого программного кода туда, где использование инструк ций недопустимо. Например, этот фрагмент программного кода созда ет список из трех функций, встраивая lambdaвыражения в литерал списка. Инструкция def не может быть вставлена в литерал, потому что это – инструкция, а не выражение. Подобные таблицы действий в языке Python можно создавать с помо щью словаря и других структур данных: >>> key = 'got' >>> {'already': (lambda: 2 + 2), ... 'got': (lambda: 2 * 4), ... 'one': (lambda: 2 ** 6) ... }[key]() 8 В данном случае, когда интерпретатор Python создает словарь, каждое из вложенных lambdaвыражений генерирует и оставляет после себя функцию для последующего использования – обращение по ключу из влекает одну из этих функций, а круглые скобки обеспечивают вызов извлеченной функции. При таком подходе словарь превращается в бо лее универсальное средство множественного выбора, чем то, что я смог реализовать на основе инструкции if в главе 12. Чтобы реализовать то же самое без использования lambdaвыражений, пришлось бы написать три отдельные инструкции def за пределами словаря, в котором эти функции используются: def f1(): return 2 + 2 def f2(): return 2 * 4 def f3(): return 2 ** 6 key = 'one' {'already': f1, 'got': f2, 'one': f3}[key]() Этот прием тоже будет работать, но ведь инструкции def могут распо лагаться в файле модуля достаточно далеко, несмотря на то, что они очень короткие. Близость программного кода, которую обеспечивают lambda выражения, особенно полезна, когда функции используются в единственном месте – если три функции в этом фрагменте не исполь зуются гдето еще, определенно имеет смысл встроить их в определе ние словаря в виде lambdaвыражений. Кроме того, инструкции def тре буют даже для маленьких функций указывать имена, а они могут вступить в конфликт с другими именами в файле модуля. 444 Глава 17. Расширенные возможности функций lambda выражения также очень удобно использовать в списках аргумен тов функций – для определения временных функций, которые больше нигде в программе не используются, – мы увидим примеры такого ис пользования ниже, в этой главе, когда будем изучать функцию map. Как (не) запутать программный код на языке Python Тот факт, что lambda должно быть единственным выражением (а не се рией инструкций), казалось бы, устанавливает серьезное ограничение на объем логики, которую можно упаковать в lambdaвыражение. Од нако если вы понимаете, что делаете, большую часть инструкций язы ка Python можно представить в форме выражений. Например, представим, что необходимо вывести некоторую информа цию из тела lambdaвыражения, тогда достаточно просто записать sys.stdout.write(str(x)+'\n’) вместо print x (в главе 11 объяснялось, что это именно то действие, которое выполняет инструкция print). Точно так же в lambdaвыражение можно заложить логику в виде трех местного выражения if/else, представленного в главе 13, или исполь зовать эквивалентную, хотя и более сложную комбинацию операторов and /or, также описанную в главе 13. Как говорилось ранее, следую щую инструкцию: if a: b else: c можно имитировать одним из следующих примерно эквивалентных выражений: b if a else c ((a and b) or c) Так как выражения, подобные этим, допустимо помещать внутрь lamb da выражения, они могут использоваться для реализации логики вы бора внутри lambdaфункций: >>> lower = (lambda x, y: x if x < y else y) >>> lower('bb', 'aa') 'aa' >>> lower('aa', 'bb') 'aa' Кроме того, если внутри lambdaвыражения потребуется выполнять циклы, их можно заменить вызовами функции map и генераторами списков (с ними мы уже познакомились в главе 13 и вернемся к ним еще раз ниже, в этой главе): >>> |