Книга на вашем родном языке 6 2 Переводы 7 1 Доступные переводы переводы 7 3 Предисловие 16
Скачать 0.72 Mb.
|
Примечание для программистов на C++/Java/C# В Python все члены класса (включая данные) являются публичными (public), а все методы – виртуальными (virtual). Исключение: Если имя переменной начинается с двойного подчёркивания, как, например, __privatevar , Python делает эту переменную приватной (private). Поэтому принято имя любой переменной, которая должна использоваться только внутри класса или объекта, начинать с подчёркивания; все же остальные имена являются публичными, и могут ис- пользоваться в других классах/объектах. Помните, что это лишь традиция, и Python вовсе не обязывает делать именно так (кроме двойного подчёркивания). 14.6 Наследование Одно из главных достоинств объектно-ориентированного программирования заключа- ется в многократном использовании одного и того же кода, и один из способов этого достичь – при помощи механизма наследования. Легче всего представить себе наследова- ние в виде отношения между классами как тип и подтип. Представим, что нам нужно написать программу, которая отслеживает информацию о преподавателях и студентах в колледже. У них есть некоторые общие характеристики: имя, возраст и адрес. Есть также и специфические характеристики, такие как зарплата, курсы и отпуск для преподавателей, а также оценки и оплата за обучение для студентов. Можно создать для них независимые классы и работать с ними, но тогда добавление какой-либо новой общей характеристики потребует добавления её к каждому из этих независимых классов в отдельности, что делает программу неповоротливой. Лучше создать общий класс с именем SchoolMember , а затем сделать так, чтобы классы преподавателя и студента наследовали этот класс, т.е. чтобы они стали подтипами этого типа (класса), после чего добавить любые специфические характеристики к этим подти- пам. У такого подхода есть множество достоинств. Если мы добавим/изменим какую-либо функциональность в SchoolMember , это автоматически отобразится и во всех подтипах. Например, мы можем добавить новое поле удостоверения для преподавателей и студен- тов, просто добавив его к классу SchoolMember . С другой стороны, изменения в подтипах 14.6. Наследование 115 A Byte of Python (Russian), Версия 2.02 никак не влияют на другие подтипы. Ещё одно достоинство состоит в том, что обращать- ся к объекту преподавателя или студента можно как к объекту SchoolMember , что может быть полезно в ряде случаев, например, для подсчёта количества человек в школе. Ко- гда подтип может быть подставлен в любом месте, где ожидается родительский тип, т.е. объект считается экземпляром родительского класса, это называется полиморфизмом. Заметьте также, что код родительского класса используется многократно, и нет необходи- мости копировать его во все классы, как пришлось бы в случае использования независи- мых классов. Класс SchoolMember в этой ситуации называют базовым классом или надклассом 3 . Классы Teacher и Student называют производными классами или подклассами 4 Рассмотрим теперь этот пример в виде программы (сохраните как inherit.py ). class SchoolMember : '''Представляет любого человека в школе.''' def __init__ ( self , name, age): self name = name self age = age print ( '(Создан SchoolMember: {0} )' format( self name)) def tell ( self ): '''Вывести информацию.''' ( 'Имя:" {0} " Возраст:" {1} "' format( self name, self age), end = " " ) class Teacher (SchoolMember): '''Представляет преподавателя.''' def __init__ ( self , name, age, salary): SchoolMember __init__ ( self , name, age) self salary = salary print ( '(Создан Teacher: {0} )' format( self name)) def tell ( self ): SchoolMember tell( self ) ( 'Зарплата: " {0:d} "' format( self salary)) class Student (SchoolMember): '''Представляет студента.''' def __init__ ( self , name, age, marks): SchoolMember __init__ ( self , name, age) self marks = marks print ( '(Создан Student: {0} )' format( self name)) def tell ( self ): SchoolMember tell( self ) (продолжение на следующей странице) 3 также «суперкласс», «родительский класс» (прим.перев.) 4 также «субкласс», «класс-наследник» (прим.перев.) 14.6. Наследование 116 A Byte of Python (Russian), Версия 2.02 (продолжение с предыдущей страницы) ( 'Оценки: " {0:d} "' format( self marks)) t = Teacher( 'Mrs. Shrividya' , 40 , 30000 ) s = Student( 'Swaroop' , 25 , 75 ) () # печатает пустую строку members = [t, s] for member in members: member tell() # работает как для преподавателя, так и для студента Вывод: $ python3 inherit.py (Создан SchoolMember: Mrs. Shrividya) (Создан Teacher: Mrs. Shrividya) (Создан SchoolMember: Swaroop) (Создан Student: Swaroop) Имя:"Mrs. Shrividya" Возраст:"40" Зарплата: "30000" Имя:"Swaroop" Возраст:"25" Оценки: "75" Как это работает: Чтобы воспользоваться наследованием, при определении класса мы указыва- ем имена его базовых классов в виде кортежа, следующего сразу за его име- нем. Далее мы видим, что метод __init__ базового класса вызывается явно при помощи переменной self , чтобы инициализировать часть объекта, отно- сящуюся к базовому классу. Это очень важно запомнить: поскольку мы опре- деляем метод __init__ в подклассах Teacher и Student , Python не вызывает конструктор базового класса SchoolMember автоматически – его необходимо вызывать самостоятельно в явном виде. Напротив, если мы не определим метод __init__ в подклассе, Python вызовет конструктор базового класса автоматически. Здесь же мы видим, как можно вызывать методы базового класса, предваряя запись имени метода именем класса, а затем передавая переменную self вме- сте с другими аргументами. Обратите внимание, что при вызове метода tell из класса SchoolMember экземпляры Teacher или Student можно использовать как экземпляры SchoolMember Заметьте также, что вызывается метод tell из подкласса, а не метод tell из класса SchoolMember . Это можно понять следующим образом: Python всегда начинает поиск методов в самом классе, что он и делает в данном случае. Если 14.6. Наследование 117 A Byte of Python (Russian), Версия 2.02 же он не находит метода, он начинает искать методы, принадлежащие базо- вым классам по очереди, в порядке, в котором они перечислены в кортеже при определении класса. Замечание по терминологии: если при наследовании перечислено более од- ного класса, это называется множественным наследованием. Параметр end используется в методе tell() для того, чтобы новая строка на- чиналась через пробел после вызова print() 14.7 Метаклассы 5 В обширной теме объектно-ориентированного программирования существует ещё много всего, но мы лишь слегка коснёмся некоторых концепций, чтобы вы просто знали об их существовании. Точно так же, как классы используются для создания объектов, можно использовать ме- таклассы для создания классов. Метаклассы существуют для изменения или добавления нового поведения в классы. Давайте рассмотрим пример. Допустим, мы хотим быть уверены, что мы всегда создаём исключительно экземпляры подклассов класса SchoolMember , и не создаём экземпляры самого класса SchoolMember Для достижения этой цели мы можем использовать концепцию под названием «абстракт- ные базовые классы». Это означает, что такой класс абстрактен, т.е. является лишь некой концепцией, не предназначенной для использования в качестве реального класса. Мы можем объявить наш класс как абстрактный базовый класс при помощи встроенного метакласса по имени ABCMeta #!/usr/bin/env python # Filename: inherit_abc.py from abc import * class SchoolMember (metaclass = ABCMeta): '''Представляет любого человека в школе.''' def __init__ ( self , name, age): self name = name self age = age print ( '(Создан SchoolMember: {0} )' format( self name)) (продолжение на следующей странице) 5 в оригинальной версии книги этот параграф невидим для читателей, так как находится в комментарии с пометкой автора «It is too sudden to introduce this concept here.», что означает «Слишком неожиданно представление этой концепции здесь.» (прим.перев.) 14.7. Метаклассы 5 118 A Byte of Python (Russian), Версия 2.02 (продолжение с предыдущей страницы) @abstractmethod def tell ( self ): '''Вывести информацию.''' ( 'Имя:" {0} " Возраст:" {1} "' format( self name, self age), end = " " ) class Teacher (SchoolMember): '''Представляет преподавателя.''' def __init__ ( self , name, age, salary): SchoolMember __init__ ( self , name, age) self salary = salary print ( '(Создан Teacher: {0} )' format( self name)) def tell ( self ): SchoolMember tell( self ) ( 'Зарплата: " {0:d} "' format( self salary)) class Student (SchoolMember): '''Представляет студента.''' def __init__ ( self , name, age, marks): SchoolMember __init__ ( self , name, age) self marks = marks print ( '(Создан Student: {0} )' format( self name)) def tell ( self ): SchoolMember tell( self ) ( 'Оценки: " {0:d} "' format( self marks)) t = Teacher( 'Mrs. Shrividya' , 40 , 30000 ) s = Student( 'Swaroop' , 25 , 75 ) #m = SchoolMember('abc', 10) # Это приведёт к ошибке: "TypeError: Can't instantiate abstract class # SchoolMember with abstract methods tell" () # печатает пустую строку members = [t, s] for member in members: member tell() # работает как для преподавателя, так и для студента Вывод: $ python3 inherit.py (Создан SchoolMember: Mrs. Shrividya) (Создан Teacher: Mrs. Shrividya) (продолжение на следующей странице) 14.7. Метаклассы 5 119 A Byte of Python (Russian), Версия 2.02 (продолжение с предыдущей страницы) (Создан SchoolMember: Swaroop) (Создан Student: Swaroop) Имя:"Mrs. Shrividya" Возраст:"40" Зарплата: "30000" Имя:"Swaroop" Возраст:"25" Оценки: "75" Как это работает: Мы можем объявить метод tell класса SchoolMember абстрактным, и таким образом автоматически запретим создавать экземпляры класса SchoolMember Тем не менее, мы можем работать с экземплярами Teacher и Student так, как будто они экземпляры SchoolMember , поскольку они являются подклассами. 14.8 Резюме Мы изучили различные аспекты классов и объектов, равно как и терминологию, связанную с ними. Мы также увидели ряд достоинств и «подводных камней» объектно-ориентированного программирования. Python – в высокой степени объектно- ориентирован, поэтому понимание этих принципов очень поможет вам в дальнейшем. Далее мы узнаем, как работать с вводом/выводом и получать доступ к файлам в Python. 14.8. Резюме 120 Ввод-вывод Рано или поздно возникают ситуации, когда программа должна взаимодействовать с пользователем. Например, принять какие-нибудь данные от пользователя, а затем вы- вести результаты. Для этого применяются функции input() и print() соответственно. Для вывода можно также использовать различные методы класса str (строка). К примеру, при помощи метода rjust можно получить строку, выравненную по правому краю к указанной ширине. См. help(str) для более подробного описания. Ещё одним распространённым типом ввода/вывода является работа с файлами. Возмож- ность создавать, читать и записывать в файлы является ключевой для многих программ, поэтому в настоящей главе и мы рассмотрим этот аспект. 15.1 Ввод от пользователя Сохраните эту программу как user_input.py : def reverse (text): return text[:: - 1 ] def is_palindrome (text): return text == reverse(text) something = input ( 'Введите текст: ' ) if (is_palindrome(something)): ( "Да, это палиндром" ) else : ( "Нет, это не палиндром" ) Вывод: $ python3 user_input.py Введите текст: сэр Нет, это не палиндром (продолжение на следующей странице) 121 A Byte of Python (Russian), Версия 2.02 (продолжение с предыдущей страницы) $ python3 user_input.py Введите текст: мадам Да, это палиндром $ python3 user_input.py Введите текст: топот Да, это палиндром Как это работает: Мы применяем операцию вырезки для переворачивания текста. Мы уже видели, как создаются вырезки из последовательностей при помощи кода « seq[a:b] », начиная с позиции a до позиции b . Но ведь мы также можем ука- зать и третий аргумент, определяющий шаг, с которым производится вырезка. По умолчанию шаг равен 1 , поэтому и возвращается непрерывный фрагмент текста. Указание отрицательного шага, т.е. -1 приведёт к выводу текста в об- ратном порядке. Функция input() принимает строку в качестве аргумента и показывает её пользователю. Затем она ждёт, чтобы пользователь набрал что-нибудь и на- жал клавишу ввода. Как только пользователь это сделал, функция input() возвращает введённый пользователем текст. Мы считываем этот текст и выстраиваем его в обратном порядке. Если пе- ревёрнутый и исходный текст одинаковы, значит введённый текст является палиндромом Домашнее задание Проверка, является ли текст палиндромом должна также игнорировать знаки пунктуа- ции, пробелы и регистр букв. Например, «А роза упала на лапу Азора» также является палиндромом, но наша текущая программа так не считает. Попробуйте улучшить её так, чтобы она распознала этот палиндром. Подсказка: (не читайте) Воспользуйтесь кортежем (список всех знаков пунктуации можно найти здесь ), содержа- щим все запрещённые символы, и примените тест на принадлежность, чтобы обнару- жить символы, подлежащие удалению, т.е. forbidden = („!“, „?“, „.“, …). 15.1. Ввод от пользователя 122 A Byte of Python (Russian), Версия 2.02 15.2 Файлы Открывать и использовать файлы для чтения или записи можно путём создания объекта класса file , а читать/записывать в файл – при помощи его методов read , readline или write соответственно. Возможность читать или записывать в файл зависит от режима, указанного при открытии файла. По окончании работы с файлом, нужно вызвать метод close 1 , чтобы указать Python, что файл больше не используется. Пример: (сохраните как using_file.py ) poem = ''' \ Программировать весело. Если работа скучна, Чтобы придать ей весёлый тон - используй Python! ''' f = open ( 'poem.txt' , 'w' ) # открываем для записи (writing) f write(poem) # записываем текст в файл f close() # закрываем файл f = open ( 'poem.txt' ) # если не указан режим, по умолчанию подразумевается # режим чтения ('r'eading) while True : line = f readline() if len (line) == 0 : # Нулевая длина обозначает конец файла (EOF) break (line, end = '' ) f close() # закрываем файл Вывод: $ python3 using_file.py Программировать весело. Если работа скучна, Чтобы придать ей весёлый тон - используй Python! Как это работает: Сперва мы открываем файл при помощи встроенной функции open с указа- нием имени файла и режима, в котором мы хотим его открыть. Режим может быть для чтения ( 'r' ), записи ( 'w' ) или добавления ( 'a' ) 2 . Можно также ука- зать, в каком виде мы будем считывать, записывать или добавлять данные: 1 close – англ. «закрывать» (прим.перев) 2 read, write и append соответственно (прим.перев.) 15.2. Файлы 123 A Byte of Python (Russian), Версия 2.02 в текстовом ( 't' ) или бинарном ( 'b' ). На самом деле существует много дру- гих режимов, и help(open) даст вам их детальное описание. По умолчанию open() открывает файл как текст в режиме для чтения. В нашем примере мы сначала открываем файл в режиме записи текста и ис- пользуем метод write файлового объекта для записи в файл, после чего за- крываем файл при помощи close Далее мы открываем тот же самый файл для чтения. В этом случае нет нужды указывать режим, так как режим «чтения текстового файла» применяется по умолчанию. Мы считываем файл построчно методом readline в цикле. Этот метод возвращает полную строку, включая символ перевода строки в конце. Когда же он возвращает пустую строку, это означает, что мы достигли конца файла, и мы прерываем цикл при помощи break По умолчанию функция print() выводит текст, автоматически добавляя сим- вол перевода строки в конце. Мы подавляем этот символ, указывая end='' , поскольку строки, считанные из файла, и без того оканчиваются символом перевода строки. И, наконец, мы закрываем файл с помощью close Теперь проверяем содержимое файла poem.txt , чтобы убедиться, что про- грамма действительно записала текст в него и считала из него. 15.3 Pickle Python предоставляет стандартный модуль с именем pickle 3 , при помощи которого мож- но сохранять |