Книга Изучаем Python
Скачать 4.68 Mb.
|
150 Глава 8 • Функции Программа создает список моделей для печати и пустой список для готовых моде- лей. Затем, поскольку обе функции уже определены, остается вызвать их и передать правильные аргументы. Мы вызываем print_models() и передаем два необходимых списка; как и ожидалось, print_models() имитирует печать моделей. Затем вызыва- ется функция show_completed_models() , и ей передается список готовых моделей, чтобы функция могла вывести информацию о напечатанных моделях. Благодаря содержательным именам функций другой разработчик сможет прочитать этот код и понять его даже без комментариев. Вдобавок эта программа создает меньше проблем с расширением и сопровождени- ем, чем версия без функций. Если позднее потребуется напечатать новую партию моделей, достаточно снова вызвать print_models() . Если окажется, что код печати необходимо модифицировать, изменения достаточно внести в одном месте, и они автоматически распространятся на все вызовы функции. Такой подход намного эффективнее независимой правки кода в нескольких местах программы. Этот пример также демонстрирует принцип, в соответствии с которым каждая функция должна решать одну конкретную задачу. Первая функция печатает каждую модель, а вторая выводит информацию о готовых моделях. Такой подход предпочтительнее решения обеих задач в функции. Если вы пишете функцию и видите, что она решает слишком много разных задач, попробуйте разделить ее код на две функции. Помните, что функции всегда можно вызывать из других функций. Эта возмож- ность может пригодиться для разбиения сложных задач на серию составляющих. Запрет изменения списка в функции Иногда требуется предотвратить изменение списка в функции. Допустим, у вас имеется список моделей для печати, и вы пишете функцию для перемещения их в список готовых моделей, как в предыдущем примере. Возможно, даже после пе- чати всех моделей исходный список нужно оставить для отчетности. Но, поскольку все имена моделей были перенесены из списка unprinted_designs , остался только пустой список; исходная версия списка потеряна. Проблему можно решить пере- дачей функции копии списка вместо оригинала. В этом случае все изменения, вносимые функцией в список, будут распространяться только на копию, а оригинал списка остается неизменным. Чтобы передать функции копию списка, можно поступить так: имя_функции(имя_списка[:]) Синтаксис среза [:] создает копию списка для передачи функции. Если удаление элементов из списка unprinted_designs в print_models .py нежелательно, функцию print_models() можно вызвать так: print_models(unprinted_designs[:], completed_models) Функция print_models() может выполнить свою работу, потому что она все равно получает имена всех ненапечатаных моделей. Но на этот раз она получает не сам список unprinted_designs , а его копию. Список completed_models заполняется Передача списка 151 именами напечатанных моделей, как и в предыдущем случае, но исходный список функцией не изменяется. Несмотря на то что передача копии позволяет сохранить содержимое списка, обыч- но функциям следует передавать исходный список (если у вас нет веских причин для передачи копии). Работа с существующим списком более эффективна, потому что программе не приходится тратить время и память на создание отдельной копии (лишние затраты особенно заметны при работе с большими списками). УПРАЖНЕНИЯ 8-9 . Фокусники: создайте список с именами фокусников . Передайте список функции show_ magicians(), которая выводит имя каждого фокусника в списке . 8-10 . Великие фокусники: начните с копии вашей программы из упражнения 8-9 . Напишите функцию make_great(), которая изменяет список фокусников, добавляя к имени каждого фокусника приставку «Great» . Вызовите функцию show_magicians() и убедитесь в том, что список был успешно изменен . 8-11 . Фокусники без изменений: начните с программы из упражнения 8-10 . Вызовите функ- цию make_great() и передайте ей копию списка имен фокусников . Поскольку исходный список остался неизменным, верните новый список и сохраните его в отдельном списке . Вызовите функцию show_magicians() с каждым списком, чтобы показать, что в одном спи- ске остались исходные имена, а в другом к имени каждого фокусника добавилась приставка «Great» . Передача произвольного набора аргументов В некоторых ситуациях вы не знаете заранее, сколько аргументов должно быть передано функции. К счастью, Python позволяет функции получить произвольное количество аргументов из вызывающей команды. Для примера рассмотрим функцию для создания пиццы. Функция должна полу- чить набор дополнений к пицце, но вы не знаете заранее, сколько дополнений за- кажет клиент. Функция в следующем примере получает один параметр *toppings , но этот параметр объединяет все аргументы, заданные в командной строке: pizza.py def make_pizza(*toppings): """Вывод списка заказанных дополнений.""" print(toppings) make_pizza('pepperoni') make_pizza('mushrooms', 'green peppers', 'extra cheese') Звездочка в имени параметра *toppings приказывает Python создать пустой кор- теж с именем toppings и упаковать в него все полученные значения. Результат команды print в теле функции показывает, что Python успешно справляется и с вызовом функции с одним значением, и с вызовом с тремя значениями. Разные вызовы обрабатываются похожим образом. Обратите внимание: Python упаковывает аргументы в кортеж даже в том случае, если функция получает всего одно значение: 152 Глава 8 • Функции ('pepperoni',) ('mushrooms', 'green peppers', 'extra cheese') Теперь команду print можно заменить циклом, который перебирает список дополнений и выводит описание заказанной пиццы: def make_pizza(*toppings): """Выводит описание пиццы.""" print("\nMaking a pizza with the following toppings:") for topping in toppings: print("- " + topping) make_pizza('pepperoni') make_pizza('mushrooms', 'green peppers', 'extra cheese') Функция реагирует соответственно независимо от того, сколько значений она получила — одно или три: Making a pizza with the following toppings: - pepperoni Making a pizza with the following toppings: - mushrooms - green peppers - extra cheese Этот синтаксис работает независимо от количества аргументов, переданных функции. Позиционные аргументы с произвольными наборами аргументов Если вы хотите, чтобы функция могла вызываться с разными количествами аргу- ментов, параметр для получения произвольного количества аргументов должен стоять на последнем месте в определении функции. Python сначала подбирает со- ответствия для позиционных и именованных аргументов, а потом объединяет все остальные аргументы в последнем параметре. Например, если функция должна получать размер пиццы, этот параметр должен стоять в списке до параметра *toppings : def make_pizza(size, *toppings): """Выводит описание пиццы.""" print("\nMaking a " + str(size) + "-inch pizza with the following toppings:") for topping in toppings: print("- " + topping) make_pizza(16, 'pepperoni') make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese') В определении функции Python сохраняет первое полученное значение в параметре size . Все остальные значения, следующие за ним, сохраняются в кортеже toppings В вызовах функций на первом месте располагается аргумент для параметра size , а за ним следует сколько угодно дополнений. Передача списка 153 В итоге для каждой пиццы указывается размер и количество дополнений, и каждый фрагмент информации выводится в положенном месте: сначала размер, а потом дополнения: Making a 16-inch pizza with the following toppings: - pepperoni Making a 12-inch pizza with the following toppings: - mushrooms - green peppers - extra cheese Использование произвольного набора именованных аргументов Иногда программа должна получать произвольное количество аргументов, но вы не знаете заранее, какая информация будет передаваться функции. В таких случаях можно написать функцию, получающую столько пар «ключ—значение», сколько указано в команде вызова. Один из возможных примеров — построение пользова- тельских профилей: вы знаете, что вы получите информацию о пользователе, но не знаете заранее, какую именно. Функция build_profile() в следующем примере всегда получает имя и фамилию, но также может получать произвольное количе- ство именованных аргументов: user_profile.py def build_profile(first, last, **user_info): """Строит словарь с информацией о пользователе.""" profile = {} profile['first_name'] = first profile['last_name'] = last for key, value in user_info.items(): profile[key] = value return profile user_profile = build_profile('albert', 'einstein', location='princeton', field='physics') print(user_profile) Определение build_profile() ожидает получить имя и фамилию пользователя, а также позволяет передать любое количество пар «имя—значение». Две звездочки перед параметром **user_info заставляют Python создать пустой словарь с име- нем user_info и упаковать в него все полученные пары «имя—значение». Внутри функции вы можете обращаться к парам «имя–значение» из user_info точно так же, как в любом словаре. В теле build_profile() создается пустой словарь с именем profile для хранения профиля пользователя. В точке в словарь добавляется имя и фамилия, потому что эти два значения всегда передаются пользователем. В точке функция пере- бирает дополнительные пары «ключ—значение» в словаре user_info и добавляет каждую пару в словарь profile . Наконец, словарь profile возвращается в точку вызова функции. 154 Глава 8 • Функции Вызовем функцию build_profile() и передадим ей имя 'albert' , фами- лию 'einstein' , и еще две пары «ключ—значение» location='princeton' и field='physics' . Программа сохраняет возвращенный словарь в user_profile и выводит его содержимое: {'first_name': 'albert', 'last_name': 'einstein', 'location': 'princeton', 'field': 'physics'} Возвращаемый словарь содержит имя и фамилию пользователя, а в данном случае еще и местонахождение и область исследований. Функция будет работать, сколько бы дополнительных пар «ключ—значение» ни было передано при вызове функции. При написании функций допускаются самые разнообразные варианты смешения позиционных, именованных и произвольных значений. Полезно знать о существо- вании всех этих типов аргументов, потому что они часто будут встречаться вам при чтении чужого кода. Только с опытом вы научитесь правильно использовать разные типы аргументов и поймете, когда следует применять каждый тип; а пока просто используйте самый простой способ, который позволит решить задачу. С течением времени вы научитесь выбирать наиболее эффективный вариант для каждой конкретной ситуации. УПРАЖНЕНИЯ 8-12 . Сэндвичи: напишите функцию, которая получает список компонентов сэндвича . Функ- ция должна иметь один параметр для любого количества значений, переданных при вызове функции, и выводить описание заказанного сэндвича . Вызовите функцию три раза с разны- ми количествами аргументов . 8-13 . Профиль: начните с копии программы user_profile .py . Создайте собственный профиль вызовом build_profile(), укажите имя, фамилию и три другие пары «ключ—значение» для вашего описания . 8-14 . Автомобили: напишите функцию для сохранения информации об автомобиле в слова- ре . Функция всегда должна возвращать производителя и название модели, но при этом она может получать произвольное количество именованных аргументов . Вызовите функцию с передачей обязательной информации и еще двух пар «имя—значение» (например, цвет и комплектация) . Ваша функция должна работать для вызовов следующего вида: car = make_car(‘subaru’, ‘outback’, color=’blue’, tow_package=True) Выведите возвращаемый словарь и убедитесь в том, что вся информация была сохранена правильно . Хранение функций в модулях Одно из преимуществ функций заключается в том, что они отделяют блоки кода от основной программы. Если для функций были выбраны содержательные имена, ваша программа будет намного проще читаться. Можно пойти еще дальше и сохра- нить функции в отдельном файле, называемом модулем, а затем импортировать модуль в свою программу. Команда import сообщает Python, что код модуля дол- жен быть доступен в текущем выполняемом программном файле. Хранение функций в отдельных файлах позволяет скрыть второстепенные детали кода и сосредоточиться на логике более высокого уровня. Кроме того, функции Хранение функций в модулях 155 можно использовать во множестве разных программ. Функции, хранящиеся в от- дельных файлах, можно передать другим программистам без распространения полного кода программы. А умение импортировать функции позволит вам исполь- зовать библиотеки функций, написанные другими программистами. Существует несколько способов импортирования модулей; все они кратко рас- сматриваются ниже. Импортирование всего модуля Чтобы заняться импортированием функций, сначала необходимо создать модуль. Модуль представляет собой файл с расширением .py , содержащий код, который вы хотите импортировать в свою программу. Давайте создадим модуль с функци- ей make_pizza() . Для этого из файла pizza .py следует удалить все, кроме функции make_pizza() : pizza.py def make_pizza(size, *toppings): """Выводит описание пиццы.""" print("\nMaking a " + str(size) + "-inch pizza with the following toppings:") for topping in toppings: print("- " + topping) Теперь создайте отдельный файл с именем making_pizzas .py в одном каталоге с pizza . py . Файл импортирует только что созданный модуль, а затем дважды вызывает make_pizza() : making_pizzas.py import pizza pizza.make_pizza(16, 'pepperoni') pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese') В процессе обработки этого файла строка import pizza приказывает Python от- крыть файл pizza .py и скопировать все функции из него в программу. Вы не види- те, как происходит копирование, потому что Python копирует код незаметно для пользователя во время выполнения программы. Вам необходимо знать одно: любая функция, определенная в pizza .py , будет доступна в making_pizzas .py Чтобы вызвать функцию из импортированного модуля, укажите имя модуля ( pizza ), точку и имя функции ( make_pizza() ), как показано в строке . Код вы- дает тот же результат, что и исходная программа, в которой модуль не импорти- ровался: Making a 16-inch pizza with the following toppings: - pepperoni Making a 12-inch pizza with the following toppings: - mushrooms - green peppers - extra cheese 156 Глава 8 • Функции Первый способ импортирования, при котором записывается команда import с име- нем модуля, открывает доступ программе ко всем функциям из модуля: имя_модуля.имя_функции() Импортирование конкретных функций Также возможно импортировать конкретную функцию из модуля. Общий синтак- сис выглядит так: from имя_модуля import имя_функции Вы можете импортировать любое количество функций из модуля, разделив их имена запятыми: from имя_модуля import функция_0, функция_1, функция_2 Если ограничиться импортированием только той функции, которую вы намерева- етесь использовать, пример making_pizzas .py будет выглядеть так: from pizza import make_pizza make_pizza(16, 'pepperoni') make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese') При таком синтаксисе использовать точечную запись при вызове функции не обя- зательно. Так как функция make_pizza() явно импортируется в команде import , при использовании ее можно вызывать прямо по имени. Назначение псевдонима для функции Если имя импортируемой функции может конфликтовать с именем существующей функции или функция имеет слишком длинное имя, его можно заменить коротким уникальным псевдонимом (alias) — альтернативным именем для функции. Псевдо- ним назначается функции при импортировании. В следующем примере функции make_pizza() назначается псевдоним mp() , для чего при импортировании используется конструкция make_pizza as mp . Ключевое слово as переименовывает функцию, используя указанный псевдоним: from pizza import make_pizza as mp mp(16, 'pepperoni') mp(12, 'mushrooms', 'green peppers', 'extra cheese') Команда import в этом примере назначает функции make_pizza() псевдоним mp() для этой программы. Каждый раз, когда потребуется вызвать make_pizza() , доста- точно включить вызов mp() — Python выполнит код make_pizza() без конфликтов с другой функцией make_pizza() , которую вы могли включить в этот файл про- граммы. Общий синтаксис назначения псевдонима выглядит так: from имя_модуля import имя_функции as псевдоним Хранение функций в модулях 157 Назначение псевдонима для модуля Псевдоним также можно назначить для всего модуля. Назначение короткого имени для модуля — скажем, p для pizza — позволит вам быстрее вызывать функ- ции модуля. Вызов p.make_pizza() получается более компактным, чем pizza. make_pizza() : import pizza as p p.make_pizza(16, 'pepperoni') p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese') Модулю pizza в команде import назначается псевдоним p , но все функции модуля сохраняют свои исходные имена. Вызов функций в записи p.make_ pizza() не только компактнее pizza.make_pizza() ; он также отвлекает внима- ние от имени модуля и помогает сосредоточиться на содержательных именах функций. Эти имена функций, четко показывающие, что делает каждая функ- ция, важнее для удобочитаемости вашего кода, чем использование полного имени модуля. Общий синтаксис выглядит так: import имя_модуля as псевдоним Импортирование всех функций модуля Также можно приказать Python импортировать каждую функцию в модуле; для этого используется оператор * : from pizza import * make_pizza(16, 'pepperoni') make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese') Звездочка в команде import приказывает Python скопировать каждую функцию из модуля pizza в файл программы. После импортирования всех функций вы сможете вызывать каждую функцию по имени без точечной записи. Тем не менее лучше не использовать этот способ с большими модулями, написанными дру- гими разработчиками; если модуль содержит функцию, имя которой совпадает с существующим именем из вашего проекта, возможны неожиданные результа- ты. Python обнаруживает несколько функций или переменных с одинаковыми именами, и вместо импортирования всех функций по отдельности происходит замена этих функций. В таких ситуациях лучше всего импортировать только нужную функцию или функ- ции или же импортировать весь модуль с последующим применением точечной записи. При этом создается чистый код, легко читаемый и понятный. Я включил этот раздел только для того, чтобы вы понимали команды import вроде следующей, когда вы встретите их в чужом коде: from имя_модуля import * |