Лутц М. - Изучаем Python (1077325), страница 102
Текст из файла (страница 102)
»> зачет([2]) [2, 1] »> зачет() [1] »> зачет() [1, 1] »> зачет() [1, 1, 1] Глава 17. Расширенные возможности функций Типичные ошибки при работе с функциями »> Сат вачвг(х=аспв): х хв Кспа: х=[] х.аррвпб(1) рг(пв х Ф Аргуиент отсутствуете и Соэлать повий список и Изиенить сбьект списка »> зачат([2]) [2, 1] »> зачат() [1] »> зачат() [1] Ф Список бопьие на растет Между прочим, инструкцию 1( в этом примере в большинстве случаев можно было бы заменить выражением х = х сг [), где используется тот факт, что оператор от в языке Ру(Ьоп возвращает один из двух объектов: если аргумент отсутствует, имя х получит значение по умолчанию Кспе, и тогда оператор сг вернет новый пустой список справа от него.
Однако это не совсем одно и то же. Если функции будет передан пустой список, оператор вернет вновь созданный список вместо полученного в аргументе, как это делает инструкция тг. (Выражение примет вид [) сг [], которое возвращает новый пустой список справа, — вер- Некоторые воспринимают такое поведение как достоинство — изменяемые аргументы по умолчанию сохраняют свое состояние между вызовами функции, поэтому они могут играть роль, подобную роли статических локальных переменных в языке С. В некотором смысле они ведут себя как глобальные переменные за исключением того, что их имена являются локальными по отношению к функциям, вследствие чего исключается конфликт имен с переменными, определенными в другом месте. Для большинства же это выглядит как недостаток, особенно для тех, кто впервые сталкивается с этой особенностью.
В языке Рув]1оп существует лучший способ сохранения состояния между вызовами функций (например, за счет использования классов, которые будут рассматриваться в шестой части книги). Кроме того, такое поведение аргументов по умолчанию сложно запомнить (и вообще понять). Они могут изменяться с течением времени. В предыдущем примере для значения по умолчанию существует единственный объект списка — тот, что был создан в момент выполнения инструкции бе(.
При каждом обращении к функции не будет создаваться новый список, поэтому он будет расти с каждым новым вызовом — он не опустошается при каждом вызове. Если такое поведение является неприемлемым, можно просто создавать копию аргумента по умолчанию в начале тела функции или переместить выражение, возвращающее значение по умолчанию, в тело функции. Поскольку в этом случае значение по умолчанию будет находиться в программном коде, который выполняется при каждом вызове функции, вы всякий раз будете получать новый объект: 476 Глава 17. Расширенные возможности функций нитесь к разделу «Проверка истинности» в главе 12, если вам не по- нятно, почему так происходит). В разных программах могут предъяв- ляться разные требования к такому поведению.
Функции, не возвращающие результат В языке Ру1)топ функции могут не иметь инструкцию гетсгп (или у1е1о). Когда функция не возвращает управление явно, выход из нее происходит, когда поток управления достигает конца тела функции. С технической точки зрения все функции возвращают некоторое значение — в отсутствие инструкции гетсгп функция автоматически возвращает объект Нэпе: »> оет ргос(х); рг1пт х а Иет возераиаемого значению еозерамается Иоле »> х = ргос('тевт(пд 123...') тевттпд 123 »> рг1пт х Иоле Такие функции, как эта, не имеющие инструкции ге(ого, представляют собой эквивалент того, что в других языках программирования называется «процедурами».
Как правило, они вызываются как инструкции, а возвращаемое значение Иоле игнорируется, поскольку они выполняют свою работу, не вычисляя результат. Об этом следует помнить, потому что интерпретатор ничего не сообщит вам, если вы попытаетесь присвоить результат функции, которая ничего не возвращает. Например, присваивание результата метода списков аррепо не вызывает появления ошибки, но при этом вы получите объект Мспе, а не обновленный список: »>Пас=[1, г, 3) »> ывт = Пвт.вррепси) »> рг1пт 1(вт Иоле а метод аррепо — зто "промерила" а метод аррепс изменяет сам список Как упоминалось в разделе «Типичные ошибки программирования» в главе 14, действие таких функций проявляется как побочный эффект и они обычно вызываются как инструкции, а не как выражения. Эта ошибка была описана в главе 16, когда мы рассматривали области видимости объемлющих функций, однако напомню еще раз: будьте внимательны при использовании переменных в области видимости объемлющей функции, которые изменяются объемлющим циклом— все ссылки на эту переменную будут запоминать значение, которое будет иметь переменная в последней итерации цикла.
Чтобы сохранить значения переменной цикла в каждой итерации, используйте аргу- Переменные цикла в обьемлющей области видимости В заключение менты со значениями по умолчанию (дополнительные сведения по этой теме вы найдете в главе 16). В заключение В этой главе мы рассмотрели расширенные понятия, связанные с функциями — 1зэЬса-выражения; функции-генераторы и инструкцию узе14; выражения-генераторы: зрр1у-подобный синтаксис вызова; инструменты функционального программирования, такие как вар, 2111ег и гесэсе; и общие правила проектирования функций. Мы также повторно рассмотрели итераторы и генераторы списков, просто потому, что они так же связаны с функциональным программированием, как и инструкции циклов.
В завершение изучения итерационных концепций мы произвели измерения производительности различных методов выполнения итераций. Наконец, мы коротко рассмотрели типичные ошибки, допускаемые прн работе с функциями, чтобы помочь вам обойти потенциальные ловушки. Эта глава завершает функциональную часть книги. В следующей части мы рассмотрим модули — вершину в иерархии структур языка Ру$Ьоп; структуру, в которой всегда и располагаются наши функции. После этого мы займемся исследованием классов — инструментов, которые являются пакетами функций со специальным первым аргументом. Как вы увидите, все, что мы здесь узнали, пригодится везде, где далее в книге будут появляться функции. Но прежде чем двинуться дальше, проверьте, насколько вы овладели основами функций, ответив на контрольные вопросы к главе н выполнив упражнения для этой части.
Закрепление пройденного Контрольные вопросы 1. Чем отличаются генераторы списков в квадратных скобках и в круглых скобках? 2. Как связаны между собой генераторы и итераторы7 3. Как узнать, является ли функция функцией-генератором7 4. Для чего служит инструкция узе1с? 5. Пусть имеются функция и кортеж аргументов, как можно было бы вызвать эту функцию7 6. Как связаны между собой функция эзр и генераторы списков2 В чем их сходства и различня2 7.
Как связаны между собой 1авсса-выражение и инструкция сег? В чем их сходства и различия? 478 Ответы 1. 2. 3. 4. б. е. Глава 17. Расширенные возможности функций Генераторы списков в квадратных скобках воспроизводят сразу весь список целиком. Когда генераторы списков заключаются в круглые скобки, они фактически превращаются в выражения-генераторы, которые имеют похожее назначение, но не воспроизводят список результатов целиком. Вместо этого выражения-генераторы возвращают объект-генератор, который поставляет по одному значению при использовании в итерационном контексте.
Генераторы — это объекты, поддерживающие итерационный протокол — они обладают методом пехт, который выполняет переход к следующему элементу в последовательности результатов и возбуждает исключение по достижении конца последовательности. В языке Ру1йоп существует возможность создавать функции-генераторы с помощью инструкции Оет, выражения-генераторы в виде генераторов списков, заключенных в круглые скобки, и объекты-генераторы с помощью классов, которые определяют специальный метод 11ег (обсуждается далее в этой книге).
Функции-генераторы имеют в своем теле инструкцию у1е1О. Во всем остальном они ничем не отличаются от обычных функций. При наличии этой инструкции интерпретатор Ру1Ьоп компилирует функцию как генератор — при вызове она возвращает объект-генератор, который поддерживает итерационный протокол. Когда запускается инструкция у1е1О, она возвращает результат вызывающей программе и приостанавливает работу функции, после этого в ответ на вызов метода пехт со стороны вызывающей программы функция возобновляет свою работу с позиции после последней выполненной инструкции у1е1О. Функции-генераторы также могут содержать инструкцию гетогп, которая завершает работу генератора. Вызвать функцию можно с использованием арр1у-подобного синтаксиса: Гопст1оп(*агратор1е). Кроме того, можно использовать встроенную функцию арр1у(топст1оп, агре), но из будущих версий Ру$Ьоп эта функция, скорее всего, будет исключена и потому такой способ нельзя считать универсальным.
Вызов функции вар напоминает генератор списков тем, что обе конструкции создают новый список с результатами, применяя операцию к каждому элементу последовательности или другого итерируемого объекта, по одному за раз. Главное различие состоит в том, что вар применяет к каждому элементу функцию, а генератор списков— произвольное выражение. Вследствие этого генераторы списков обладают большей гибкостью — они могут применять функцию, как и вар, а функция вар требует, чтобы применяемое выражение было оформлено в виде функции. Кроме того, генераторы списков поддерживают расширенный синтаксис. Например, вложенные циклы тог и условные операторы зт, что делает их похожими на встроенную функцию Гз1тег.