Шуман Х. - Python для детей - 2019. # Startwerte festlegen Red (255,0,0)
Скачать 5.95 Mb.
|
Глава Функции 5 106 Рис. 5.3.Что-то пошло не так Подожди-ка, здесь что-то не так. Ничего не поменялось. Почему? Давай для проверки временно вставим инструк- цию print в функцию exchange(): def exchange(x1, x2) : Swap = x1 x1 = x2 x2 = Swap print ("Меняем " + str(x1) + " и " + str(x2)) Программа, запускаемая с теми же числами, что и ранее, выдаст следующий результат (рис. 5.4): Рис. 5.4.Результат работы программы Два числа поменялись местами, но результат неочевиден. Обмен значений 107 Все это имеет простую причину: функция не получает в качестве параметра само значение переменной, а только копию ее зна- чения. Исходное значение переменной не изменяется. Внутри функции изменяется только значение копии. И оно удаляется пос- ле выхода из функции. Как насчет инструкции return? Будет ли она работать с дву- мя значениями? Давай попробуем: def exchange(x1, x2) : Swap = x1 x1 = x2 x2 = Swap return x1, x2 Если мы так изменим исходный код, у нас не будет сообще- ния об ошибке. Фактически инструкция return может вер- нуть два (или более) значения. Это особенность языка программирования Python. Другие извест- ные языки программирования, такие как C ++, C # или Java, могут возвращать только одно значение с помощью инструкции return. Для этого есть возможность сослаться на переменную как на па- раметр, а не копию, чтобы изменить оригинальное значение. И как же нам использовать это в основной программе? Да- вай посмотрим (⇒ swap2.py): # Основная программа print("Введи число: ", end="") Number1 = int(input()) print ("И еще одно : ", end="") Number2 = int(input()) print ("До обмена: " + str(Number1) + " и " + str(Number2)) Number1, Number2 = exchange(Number1, Number2) print ("После обмена: " + str(Number1) + " и " + str(Number2)) Здесь уже нет смысла повторно использовать переменные Number1 и Number2. ¾ Введи исходный код и попробуй запустить программу с парой чисел (рис. 5.5). Глава Функции 5 108 Рис. 5.5.Исправленная версия программы Как видишь, в Python ты можешь выполнять несколько заданий квазипараллельно. Поэтому, если ты захочешь присвоить значение нескольким переменным, это делается так: Number = 5 Text = "Привет" Или так: Number, Text = 5, "Привет" Это работает, даже если используются переменные разных типов. Инструкция print может использоваться для проверки того, что они являются переменными одного типа: (str(Number), Text) Сортировка чисел Я сейчас вернусь немного назад – в нашу игру-лотерею. Там у нас была серия случайных чисел, сгенерированных в на- дежде, что некоторые из них в итоге будут «правильными». При выводе чисел мне все-таки не нравится то, что это про- исходит вразнобой. Аккуратная строка отсортированных чисел выглядела бы в этой игре намного лучше, как счи- таешь? Для этого нам нужно собрать все числа из списка, а затем передать их функции сортировки. Простой процесс сортировки ты можешь увидеть в следующем примере: Сортировка чисел 109 def bubblesort(x, Index) : for i in range(Index) : for j in range(Index-i-1) : if x[j] > x[j+1] : x[j], x[j+1] = exchange(x[j], x[j+1]); В качестве параметра функция принимает список отсор- тированных номеров и количество элементов для сорти- ровки. bubblesort() – это метод сортировки, в котором отдельные значения как бы поднимаются, как пузыри (от англ. bubb le – пузырик), до тех пор, пока не будут отсортированы по размеру. Это происходит в двух циклах for: – во внутреннем цикле два числа сравниваются и меня- ются местами, если первое число больше второго. Это повторяется, пока обмен необходим и возможен. Для обмена мы можем использовать функцию exchange(), которую обсуждали ранее; – после каждой итерации внешнего цикла, подсчитыва- ющего элементы, внутренний цикл укорачивается (чем меньше значение Indexi1, чем больше значение i). Во время процесса сортировки меньшие числа как бы «скользят» все дальше и дальше вперед, как пузырьки в во- де движутся на поверхность. ¾ Чтобы проверить эту сортировку, введи следующий код или измени свою старую версию игры (⇒ lotto4.py): import random # Функция def exchange(x1, x2) : Swap = x1 x1 = x2 x2 = Swap return x1, x2 def bubblesort(x, Index) : for i in range(Index) : for j in range(Index-i-1) : if x[j] > x[j+1] : x[j], x[j+1] = exchange(x[j], x[j+1]); # Основная программа Ball = [] Lotto = [0,0,0,0,0,0] # Все шары еще не "вытащены" for Nr in range(1,50) : Глава Функции 5 110 Ball.append(False) Case = random.randint(1,49) # "Вытягивается" шесть шаров for Nr in range(6) : # поиск неиспользованных шаров while Ball[Case] : Case = random.randint(1,49) # Пометка использованного шара "вытащенным" Ball[Case] = True Lotto[Nr] = Case # Сортировка и отображение bubblesort(Lotto,6) print(Lotto) Сначала мы определим две функции, которые хотим ис- пользовать, и одну из них в основной программе. Там мы создадим два списка: Ball = [] Lotto = [0,0,0,0,0,0] Один из них остается пустым, а другой заполним шестью нулями. Далее будут добавлены лотерейные номера. Это выполняется следующим образом: Ball[Case] = True Lotto[Nr] = Case Ball – это число в лотерее, помечаемое как выпавшее, и но- вое случайное число добавляется в список Lotto. Как ви- дишь, функция append() имеет смысл только тогда, когда в список добавляется новое число. В конце эти числа сортируются (шесть номеров лотереи), а в качестве второго параметра указываются 6 выбранных чисел, формируя список выпавших чисел: bubblesort(Lotto,6) print(Lotto) Сортировка чисел 111 Рис. 5.6.Числа отсортированы и выведены в виде строки В конце есть кое-что удивительное. С помощью функ- ции print() выводится содержимое списка в виде строки. Но если ты хочешь вывести их так же, как мы делали ра- нее в игре-лотерее, тебе нужно будет заменить последнюю строку на следующий дополнительный цикл: for Nr in range(6) : print("№ " + str(Nr+1) + " => " + str(Lotto[Nr])) Ничего не заметил? Кажется, что список, подобный Lotto, также является глобальным в функции сортировки, по крайней мере мы не использовали инструкцию return, а в конце все равно все от- сортировалось. Это происходит из-за списков, которые передаются в качестве параметров, а не копии, поскольку они иногда могут быть очень большими. Вместо этого вы работаете с так называемой ссылкой: функция обращается непосредственно к содержимому списка (а не к его копии). Поэтому операция, применяемая к списку в функции, например сортировка, также работает и вне функции. Наконец, существует метод «сортировки» для списков – sort . С его помощью мы можем упростить код и поместить следующую строку перед последней инструкцией print: Lotto.sort() Даже в этом случае список, о котором идет речь, сортирует- ся. Тогда мне не пришлось бы здесь использовать ссылки, потому что, как ты уже знаешь, списки в качестве парамет- Глава Функции 5 112 ров отличаются от обычных переменных. Таким образом, теперь ты умеешь сортировать элементы. Подведение итогов Прежде чем отдохнуть, я хочу понять, что ты усвоил. В этой главе мы рассмотрели не так много нового, но все это до- статочно важно, потому что ты теперь сможешь определить любую (почти любую) функцию: def Определение функции return Выход из функции global Объявление переменной как глобальной sort() Сортировка элементов в списке Несколько вопросов... 1. В чем основное различие между глобальными и ло- кальными переменными? 2. Могут ли параметры быть переменными? ...и задач 1. Измени программу обмена так, чтобы менять местами две строки вместо двух чисел. 2. Необходимо определить сумму целых чисел от 1 до пре- дела. Напиши программу. 3. Вычисли среднее из целых чисел от 1 до предела. Напи- ши программу. 4. Измени предыдущие проекты: в каких из них можно использовать функции? 113 6 Классы и модули В предыдущих главах ты написал довольно много кода. Если ты продолжишь в том же духе, исходный код станет все более сложным. Конечно, некоторые фрагменты мож- но объединить в функции, но если программы содержат не тридцать строк, а более ста или даже тысячи, возникает во- прос, нельзя ли скомбинировать функциональность в ме- тоды, или разделить их, чтобы сохранить общую картину. Конечно, Python предлагает соответствующие конструк- ции, о которых ты узнаешь в этой книге. Речь пойдет о так называемом объектно-ориентированном программирова- нии (сокращенно ООП). Итак, в этой главе ты узнаешь: как написать свой собственный класс; что такое инкапсуляция и наследование; как разделить программу на несколько модулей; кое-что о публичности и приватности. Потомки Начнем с малого, чтобы лучше понять, что такое объект но- ориентированное программирование. Как видно из названия, речь идет об объектах. Это «объек- ты», которые всегда окружают нас. Так, например, дома, де- Глава Классы и модули 6 114 ревья, автомобили, люди. Ты – это тоже объект. Аналогично и в Python есть объекты, но, разумеется, они виртуальные. Концепция заключается в обобщении данных и функций. Вместо данных можно также сказать атрибуты и свойства. А функции здесь можно назвать методами. Они использу- ются для обработки данных или для обработки атрибутов. Все характеристики (т. е. атрибуты, методы) аналогичных объектов суммируются в так называемые классы. В класс собран весь функционал, на который способен объект. Это называется инкапсуляцией (рис. 6.1). Рис. 6.1.Инкапсуляция на примере задорного мужичка Ты уже использовал такой класс несколько раз. Это класс списка. Вот так, например, будет выглядеть объект списка: List = [] Он также называется экземпляром класса списка. В нашем примере он пустой. Чтобы добавить элемент, необходим метод append(): Потомки 115 List.append(Element) Класс списка допускает использование дополнительных методов, в том числе: # Вставить элемент в определенной позиции List.insert(Nr, Element) # Удалить определенный элемент List.remove(Element) # Значение элемента списка Value = len(List) Теперь мы можем сами написать собственный класс. Он может быть связан с математикой, но не обязательно. Да- вай вернемся на пару сотен лет назад и посетим некоего доктора Франкенштейна. Он разработал аналогичный класс: class Monster : pass Для чего нужно слово pass? Это просто заполнитель. Потому что в Python конструкцция с двоеточием (:) в конце никог- да не может быть пустой. Поскольку в нашем мини-клас- се все еще нет элементов, вставим слово pass. Мы заменим его, как только класс получит свои атрибуты и методы. Это очень простой монстр, у которого есть имя и сущность в качестве атрибута. Существует также подходящий метод. У доктора Франкенштейна не было такого удобного спосо- ба, чтобы проверить свое творение, как у нас. Рассмотрим нашего милого монстра. Давай соберем все вместе: class Monster : # Атрибут Name = "Фрэнки" Character = "необычный" # Метод def show(self) : print("Имя: " + Name) print("Особенность: " + Character) В отображаемом методе show два атрибута получают одина- ковое значение. Глава Классы и модули 6 116 ИмяКласса Атрибут (Переменные) Методы (Самостоятельные функции) : class В чем разница между функцией и методом? Прежде всего они определяются по-разному. Хотя функция допустима и используется повсюду, метод привязан к объекту. Вот по- чему они совершенно разные. Давай посмотрим, как вы- глядит основная часть программы: Frank = Monster() Frank.show() Во-первых, создается объект, то есть экземпляр класса Mon ster . Скобки после имени класса важны. Для доступа к ме- тоду show() необходимо указать имя объекта (в нашем при- мере – Frank). Объект Объект = Класс() .Метод(Параметр) Выше показано, что методы могут также иметь параметры. Точка (.), которая соединяет объект и связанный с ним ме- тод, тоже важна. Если ты запустишь нашу небольшую программу, то, к со- жалению, будешь разочарован сообщением об ошибке (рис. 6.2). self и __init__ 117 Рис. 6.2.Сообщение об ошибке: имя Name не определено Почему имя не определяется? Объект не знает свои соб- ственные атрибуты? То же самое произойдет с атрибутом. Как решить эту проблему? self и __init__ Прежде всего нужно знать, что при определении класса ме- тоды доступны однократно. Если, например, ты создаешь пять объектов типа Monster, атрибуты Name и Entity использу- ются пять раз, а метод show() – только один раз. Хотя атри- буты теперь «находятся» в объектах (и могут иметь разные значения для каждого объекта), методы остаются в преде- лах класса. Чтобы метод получил доступ к атрибуту объекта, он должен быть связан с атрибутами с помощью первого параметра – self и только в определении класса. Посмотри, как меняет- ся наш код (⇒ monster1.py): class Monster : # Атрибут Name = "Фрэнки" Character = "необычный" # Метод def show(self) : print("Имя: " + self.Name) print("Особенность: " + self.Character) # Основная программа Frank = Monster() Frank.show() Глава Классы и модули 6 118 Методу show передается первый (только здесь) параметр, self . Кроме того, отдельные атрибуты связаны через self и точку. Код основной программы не меняется, метод show() по-прежнему вызывается без параметров. Если метод связан с несколькими параметрами, первым всегда должен указываться self. Имя параметр БлокИнструкций (self, ) : def Кроме того, все атрибуты доступны только через self, на- пример: self.Атрибут Результат|Формула = Все это работает только внутри класса, но не вне. ¾ Создай программу с показанным выше исходным ко- дом. Сохрани программу под именем monster1.py, а за- тем запусти ее (рис. 6.3). Рис. 6.3.Теперь программа работает Теперь монстр Frank откликается на свое имя и имеет соб- ственный характер. Далее мы установим значения двух атрибутов. Попробуем сделать метод более гибким, если эти значения использо- вать только при создании объекта. Для этого нам нужно расширить наше определение класса (⇒ monster2.py): self и __init__ 119 class Monster : # Инициализация атрибута def __init__(self, name, character) : self.Name = name self.Character = character # Метод def show(self) : print("Имя: " + self.Name) print("Особенность: " + self.Character) # Основная программа Frank = Monster("Фрэнки", "необычный") Frank.show() ¾ Измени исходный код своей программы и запусти ее. Как ты видишь, мы получили тот же результат, что и раньше. Что изменилось? Первое, что ты заметишь, – это добавлен- ный новый метод. Метод __init__ иногда называют кон- структором, потому что он вызывается автоматически при создании (конструировании) объекта. А зачем нужно так много «черточек»? Имя метода __init__ со- держит два символа подчеркивания (_) до и после своего слова init , которые вводятся сочетанием клавиш Shift+– (клавиша пос- ле 0 (нуля)). Они необходимы, а для чего – я расскажу позже. В рамках метода __init__ двум атрибутам присваивается значение: def __init__(self, name, character) : self.Name = name self.Character = character По этой причине при создании объекта в основной про- грамме требуется два параметра: Frank = Monster("Фрэнки", "необычный") Конечно, можно было бы одновременно сгенерировать сразу два объекта, например так: Albert = GMonster("Альберт", "задумчивый") Глава Классы и модули 6 120 Ты можешь попытаться избавиться от «черточек» и просто запус- тить метод init(). Когда ты запустишь программу, то получишь ошибку, показанную на рис. 6.4. Рис. 6.4. Ошибка передачи аргументов Во время создания не получается использовать какие-либо пара- метры. Но это можно решить следующим образом: Frank = Monster() Frank.init("Фрэнки", "необычный") Frank.show() В этом случае метод init() должен быть вызван отдельно. Наследование Доктор Франкенштейн планирует создать еще двух мон- стриков. Конечно, мы хотим на них взглянуть. Сначала можно подумать, что нужно создать один и тот же класс три раза, каждый раз с другим именем. Но есть более короткий способ (⇒ monster3.py): class Monster : # Инициализация атрибута def __init__(self, name, character) : self.Name = name self.Character = character # Метод def show(self) : print("Имя: " + self.Name) print("Особенность: " + self.Character) class GMonster (Monster): pass Наследование 121 class SMonster (Monster): pass И больше ничего? Нет. Достаточно указать имя уже опре- деленного класса в первой строке, например в виде пара- метра в круглых скобках. Будет создан производный класс. Другими словами, это дочерний класс, и он наследует все элементы родительского класса (рис. 6.5). Рис. 6.5.Наследование на примере задорного мужичка Кстати, ты мог создать класс Monster следующим образом: class Monster(object) : В более ранних версиях Python это было обычным явлением, что новый класс автоматически производился из объекта «родитель- ского класса». В основной программе теперь может быть создано три (разных) монстра: Frank = Monster("Фрэнки", "необычный") Frank.show() Albert = GMonster("Альберт", "задумчивый") Albert.show() Sigmund = SMonster("Зигмунд", "веселый") Sigmund.show() ¾ Напечатав код, а затем запустив программу, ты полу- чишь результат, показанный на рис. 6.6. |