Мэтиз. Изучаем Python. Crash course2 n d e d i t i o na h a n d s o n, p r o j e c t b a s e d i n t r o d u c t i o n t o p r o g r a m m i n g
Скачать 6.2 Mb.
|
8.14. Автомобили: напишите функцию для сохранения информации об автомобиле в сло- варе. Функция всегда должна возвращать производителя и название модели, но при этом она может получать произвольное количество именованных аргументов. Вызовите функ- цию с передачей обязательной информации и еще двух пар «имя-значение» (например, цвет и комплектация). Ваша функция должна работать для вызовов следующего вида: car = make_car('subaru', 'outback', color='blue', tow_package=True) Выведите возвращаемый словарь и убедитесь в том, что вся информация была сохранена правильно. Хранение функций в модулях Одно из преимуществ функций заключается в том, что они отделяют блоки кода от основной программы. Если для функций были выбраны содержательные имена, 164 Глава 8 • Функции ваша программа будет намного проще читаться. Можно пойти еще дальше и сохра- нить функции в отдельном файле, называемом модулем, а затем импортировать модуль в свою программу. Команда import сообщает Python, что код модуля должен быть доступен в текущем выполняемом программном файле. Хранение функций в отдельных файлах позволяет скрыть второстепенные детали кода и сосредоточиться на логике более высокого уровня. Кроме того, функции можно использовать во множестве разных программ. Функции, хранящиеся в от- дельных файлах, можно передать другим программистам без распространения полного кода программы. А умение импортировать функции позволит вам исполь- зовать библиотеки функций, написанные другими программистами. Существует несколько способов импортирования модулей; все они кратко рас- сматриваются ниже. Импортирование всего модуля Чтобы заняться импортированием функций, сначала необходимо создать модуль. Мо- дуль представляет собой файл с расширением .py , содержащий код, который вы хотите импортировать в свою программу. Давайте создадим модуль с функцией make_pizza() Для этого из файла pizza .py следует удалить все, кроме функции make_pizza() : pizza.py def make_pizza(size, *toppings): """Выводит описание пиццы.""" print(f"\nMaking a {size}-inch pizza with the following toppings:") for topping in toppings: print(f"- {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() ), как показано в строке . Код выдает тот же результат, что и исходная программа, в которой модуль не импортировался: Хранение функций в модулях 165 Making a 16-inch pizza with the following toppings: - pepperoni Making a 12-inch pizza with the following toppings: - mushrooms - green peppers - extra cheese Первый способ импортирования, при котором записывается команда import с именем модуля, открывает доступ программе ко всем функциям из модуля. Если вы используете эту разновидность команды import для импортирования всего модуля имя_модуля .py , то каждая функция модуля будет доступна в следующем синтаксисе: имя_модуля.имя_функции() Импортирование конкретных функций Также возможно импортировать конкретную функцию из модуля. Общий синтак- сис выглядит так: 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 переименовывает функцию, используя указанный псевдоним: 166 Глава 8 • Функции 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 псевдоним Назначение псевдонима для модуля Псевдоним также можно назначить для всего модуля. Назначение короткого имени для модуля — скажем, 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 в файл программы. После импортирования всех функций вы сможете Стилевое оформление функций 167 вызывать каждую функцию по имени без точечной записи. Тем не менее лучше не использовать этот способ с большими модулями, написанными другими разработ- чиками; если модуль содержит функцию, имя которой совпадает с существующим именем из вашего проекта, возможны неожиданные результаты. Python обнару- живает несколько функций или переменных с одинаковыми именами, и вместо импортирования всех функций по отдельности происходит замена этих функций. В таких ситуациях лучше всего импортировать только нужную функцию или функ- ции или же импортировать весь модуль с последующим применением точечной записи. При этом создается чистый код, легко читаемый и понятный. Я включил этот раздел только для того, чтобы вы понимали команды import вроде следующей, когда вы встретите их в чужом коде: from имя_модуля import * Стилевое оформление функций В стилевом оформлении функций необходимо учитывать некоторые подробности. Функции должны иметь содержательные имена, состоящие из букв нижнего реги- стра и символов подчеркивания. Содержательные имена помогают вам и другим разработчикам понять, что же делает ваш код. Эти соглашения следует соблюдать и в именах модулей. Каждая функция должна быть снабжена комментарием, который кратко поясняет, что же делает эта функция. Комментарий должен следовать сразу же за определе- нием функции в формате строк документации. Если функция хорошо документи- рована, другие разработчики смогут использовать ее, прочитав только описание. Конечно, для этого они должны доверять тому, что код работает в соответствии с описанием, но если знать имя функции, то, какие аргументы ей нужны и какое значение она возвращает, они смогут использовать ее в своих программах. Если для параметра задается значение по умолчанию, слева и справа от знака ра- венства не должно быть пробелов: def имя_функции(параметр_0, параметр_1='значение_по_умолчанию') Те же соглашения должны применяться для именованных аргументов в вызовах функций: имя_функции(значение_0, параметр_1='значение') Документ PEP 8 ( https://www .python .org/dev/peps/pep-0008/ ) рекомендует ограни- чить длину строк кода 79 символами, чтобы строки были полностью видны в окне редактора нормального размера. Если из-за параметров длина определения функ- ции превышает 79 символов, нажмите Enter после открывающей круглой скобки в строке определения. В следующей строке дважды нажмите Tab, чтобы отделить список аргументов от тела функции, которое должно быть снабжено отступом только на один уровень. 168 Глава 8 • Функции Многие редакторы автоматически выравнивают дополнительные строки парамет- ров по отступам, установленным в первой строке: def имя_функции( параметр_0, параметр_1, параметр_2, параметр_3, параметр_4, параметр_5): тело функции... Если программа или модуль состоят из нескольких функций, эти функции можно разделить двумя пустыми строками. Так вам будет проще увидеть, где кончается одна функция и начинается другая. Все команды import следует записывать в начале файла. У этого правила есть только одно исключение: файл может начинаться с комментариев, описывающих программу в целом. УПРАЖНЕНИЯ 8.15. Печать моделей: выделите функции примера print_models .py в отдельный файл с име- нем printing_functions .py . Разместите команду import в начале файла print_models .py и измени- те файл так, чтобы в нем использовались импортированные функции. 8.16. Импортирование: возьмите за основу одну из написанных вами программ с одной функцией. Сохраните эту функцию в отдельном файле. Импортируйте функцию в файл основной программы и вызовите функцию каждым из следующих способов: import имя_модуля from имя_модуля import имя_функции from имя_модуля import имя_функции as псевдоним import имя_модуля as псевдоним from имя_модуля import * 8.17. Стилевое оформление функций: выберите любые три программы, написанные для этой главы. Убедитесь в том, что в них соблюдаются рекомендации стилевого оформления, представленные в этом разделе. Итоги В этой главе вы научились писать функции и передавать аргументы, в которых функциям передается информация, необходимая для их работы. Вы узнали, как использовать позиционные и именованные аргументы и как передать функции про- извольное количество аргументов. Вы видели функции, которые выводят данные, и функции, которые возвращают значения. Вы научились использовать функции со списками, словарями, командами if и циклами while . Также вы научились со- хранять функции в отдельных файлах, называемых модулями, чтобы код ваших программ стал проще и понятнее. Глава завершается рекомендациями по стилевому оформлению функций, чтобы ваши программы были хорошо структурированы и легко читались вами и другими разработчиками. Итоги 169 Каждый программист должен стремиться к написанию простого кода, который справляется с поставленной задачей, и функции помогают вам в этом. Вы смо- жете писать блоки кода и оставлять их на будущее. Когда вы знаете, что функция правильно справляется со своей задачей, считайте, что она работает, и переходите к следующей задаче. В программе с использованием функций единожды написанный код может заново использоваться столько раз, сколько потребуется. Чтобы выполнить код, содержа- щийся в функции, достаточно написать всего одну строку с вызовом, а функция сделает все остальное. Если же потребуется модифицировать поведение функции, достаточно внести изменения всего в одном месте; они вступят в силу повсюду, где вызывается эта функция. С функциями ваши программы проще читаются, а хорошо выбранные имена функ- ций описывают, что делает та или иная часть программы. Прочитав серию вызовов функций, вы гораздо быстрее поймете, что делает функция, чем при чтении длин- ной серии программных блоков. Функции также упрощают тестирование и отладку кода. Когда основная рабо- та программы выполняется набором функций, каждая из которых решает одну конкретную задачу, вам будет намного проще организовать тестирование и со- провождение вашего кода. Напишите отдельную программу, которая вызывает каждую функцию и проверяет ее работоспособность во всех типичных ситуациях. В этом случае вы можете быть уверены в том, что ваши функции всегда работают правильно. В главе 9 вы научитесь писать классы. Классы объединяют функции и данные в один удобный пакет, с которым программист может работать гибко и эффективно. 9 Классы Объектно-ориентированное программирование по праву считается одной из самых эффективных методологий создания программных продуктов. В объ- ектно-ориентированном программировании вы создаете классы, описывающие реально существующие предметы и ситуации, а затем создаете объекты на основе этих описаний. При написании класса определяется общее поведение для целой категории объектов. Когда вы создаете конкретные объекты на базе этих классов, каждый объект автоматически наделяется общим поведением; после этого вы можете наделить каждый объект уникальными особенностями на свой выбор. Просто невероятно, насколько хорошо реальные ситуации моделируются в объектно-ориентированном программировании. Создание объекта на основе класса называется созданием экземпляра; таким об- разом, вы работаете с экземплярами класса. В этой главе вы будете писать классы и создавать экземпляры этих классов. Вы будете указывать, какая информация может храниться в экземплярах, и определять действия, которые могут выпол- няться с экземплярами. Также вы будете писать классы, расширяющие функцио- нальность существующих классов; это позволяет организовать эффективное совместное использование кода похожими классами. Вы будете сохранять классы в модулях и импортировать классы, написанные другими программистами, в ваши программные файлы. С хорошим пониманием объектно-ориентированного программирования вы взгля- нете на мир с точки зрения программиста. Вам будет проще понять свой код — увидеть не только то, что происходит в каждой его строке, а более масштабные концепции, лежащие в его основе. Логика, заложенная в основу классов, научит вас мыслить логически, чтобы ваши программы эффективно решали практически любые задачи, с которыми вы можете столкнуться. Кроме того, классы упрощают жизнь вам и другим программистам, с которыми вам придется работать совместно над более серьезными проектами. Когда вы и другие программисты пишете код, базирующийся на сходной логике, вам будет проще разобраться в коде, написанном другими людьми. Ваши программы будут понятны коллегам, и в результате все вы сможете добиться больших результатов. Создание и использование класса 171 Создание и использование класса Классы позволяют моделировать практически все что угодно. Начнем с написания простого класса Dog , представляющего собаку — не какую-то конкретную, а собаку вообще. Что мы знаем о собаках? У них есть кличка и возраст. Также известно, что большинство собак умеют садиться и перекатываться по команде. Эти два вида информации (кличка и возраст) и два вида поведения (сидеть и перекатываться) будут включены в класс Dog , потому что они являются общими для большинства собак. Класс сообщает Python, как создать объект, представляющий собаку. По- сле того как класс будет написан, мы используем его для создания экземпляров, каждый из которых представляет одну конкретную собаку. Создание класса Dog В каждом экземпляре, созданном на основе класса Dog , будет храниться кличка ( name ) и возраст ( age ); кроме того, в нем будут присутствовать методы sit() и roll_over() : dog.py ❶ class Dog(): ❷ """Простая модель собаки.""" ❸ def __init__(self, name, age): """Инициализирует атрибуты name и age.""" ❹ self.name = name self.age = age ❺ def sit(self): """Собака садится по команде.""" print(f"{self.name} is now sitting.") def roll_over(self): """Собака перекатывается по команде.""" print(f"{self.name} rolled over!") В этом коде есть много мест, заслуживающих вашего внимания, но не беспокойтесь. Эта структура неоднократно встретится вам в этой главе, и вы еще успеете к ней привыкнуть. В точке определяется класс с именем Dog . По общепринятым согла- шениям имена, начинающиеся с символа верхнего регистра, в Python обозначают классы. Круглые скобки в определении класса пусты, потому что класс создается с нуля. В точке приведена строка документации с кратким описанием класса. Метод __init__() Функция, являющаяся частью класса, называется методом. Все, что вы узнали ранее о функциях, также относится и к методам; единственное практическое раз- личие — способ вызова методов. Метод __init__() в точке — специальный метод, 172 Глава 9 • Классы который автоматически выполняется при создании каждого нового экземпляра на базе класса Dog . Имя метода начинается и заканчивается двумя символами подчер- кивания; эта схема предотвращает конфликты имен стандартных методов Python и методов ваших классов. Будьте внимательны: два символа подчеркивания долж- ны стоять на каждой стороне __init__() . Если вы поставите только один символ подчеркивания с каждой стороны, то метод не будет вызываться автоматически при использовании класса, что может привести к появлению коварных ошибок. Метод __init__() определяется с тремя параметрами: self , name и age . Пара- метр self обязателен в определении метода; он должен предшествовать всем остальным параметрам. Он должен быть включен в определение, потому что при будущем вызове метода __init__() (для создания экземпляра Dog ) Python автоматически передает аргумент self . При каждом вызове метода, связанного с классом, автоматически передается self — ссылка на экземпляр; она предо- ставляет конкретному экземпляру доступ к атрибутам и методам класса. Когда вы создаете экземпляр Dog , Python вызывает метод __init__() из класса Dog . Мы передаем Dog() кличку и возраст в аргументах; значение self передается автома- тически, так что его передавать не нужно. Каждый раз, когда вы захотите создать экземпляр на основе класса Dog , необходимо предоставить значения только двух последних аргументов, name и age Каждая из двух переменных, определяемых в точке , снабжена префиксом self Любая переменная с префиксом self доступна для каждого метода в классе, и вы также сможете обращаться к этим переменным в каждом экземпляре, созданном на основе класса. Конструкция self.name = name берет значение, хранящееся в па- раметре name , и сохраняет его в переменной name , которая затем связывается с соз- даваемым экземпляром. Процесс также повторяется с self.age = age . Переменные, к которым вы обращаетесь через экземпляры, также называются атрибутами. В классе Dog также определяются два метода: sit() и roll_over() . Так как этим методам не нужна дополнительная информация (кличка или возраст), они определяются с единственным параметром self . Экземпляры, которые будут созданы позднее, смогут вызывать эти методы. Пока методы sit() и roll_over() ограничиваются простым выводом сообщения о том, что собака садится или перекатывается. Тем не менее концепцию легко расширить для практического применения: если бы этот класс был частью компьютерной игры, то эти методы вполне могли бы содержать код для создания анимации садящейся или пере- катывающейся собаки. А если бы класс был написан для управления роботом, то методы могли бы управлять механизмами, заставляющими робота-собаку выполнить соответствующую команду. Создание экземпляра класса Считайте, что класс — это своего рода инструкция по созданию экземпляров. Со- ответственно класс Dog — инструкция по созданию экземпляров, представляющих конкретных собак. |