Книга на вашем родном языке Переводы 7 1 Доступные переводы переводы 7 3 Предисловие 16 1
Скачать 0.91 Mb.
|
Пример: сохраните как oop_init.py ) class Person : def __init__ ( self , name): self Привет Меня зовут name) p = Person( 'Swaroop' ) p say_hi() # Предыдущие 2 строки можно Вывод python Привет Меня зовут Как это работает: Здесь мы определяем метод __init__ так, чтобы он принимал параметр наряду с обычным self ). Далее мы создаём новое поле с именем name . Обратите внимание, что это две разные переменные, даже несмотря на то, что они обе названы name . Это не проблема, так как точка в выражении self. name обозначает, что существует нечто с именем «name», являющееся частью объекта «self», и другое name – локальная переменная. Поскольку мы в явном виде указываем, к которому имени мы обращаемся, путаницы не возникнет. Для создания нового экземпляра класса мы указываем имя класса, после которого – аргументы в скобках = Person('Swaroop') Метод __init__ мы при этом не вызываем явным образом. В этом и заключается специальная роль данного метода. После этого мы получаем возможность использовать поле self.name в наших методах, что и продемонстрировано в методе say_hi 14.4. Метод A Byte of Python (Russian), Версия 2.02 14.5 Переменные класса и объекта Функциональную часть классов и объектов (те. методы) мы обсудили, теперь давайте ознакомимся счастью данных. Данные, те. поля, являются нечем иным, как обычными переменными, заключёнными в пространствах имён классов и объектов. Это означает, что их имена действительны только в контексте этих классов или объектов. Отсюда и название пространство имён». Существует два типа полей переменные класса и переменные объекта, которые различаются в зависимости оттого, принадлежит ли переменная классу или объекту соответ- ственно. Переменные класса разделяемы – доступ к ним могут получать все экземпляры этого класса. Переменная класса существует только одна, поэтому когда любой из объектов изменяет переменную класса, это изменение отразится и во всех остальных экземплярах того же класса. Переменные объекта принадлежат каждому отдельному экземпляру класса. В этом случае у каждого объекта есть своя собственная копия поля, те. не разделяемая и никоим образом несвязанная с другими такими же полями в других экземплярах. Это легко понять на примере (сохраните как Представляет робота с именем Переменная класса, содержащая количество роботов, Инициализация данных name = name Инициализация name)) # При создании этой личности, робот добавляется к переменной 'population' Robot Я умираю.''' ( ' {0} уничтожается!' format( self name)) Robot population -= 1 if Robot был последним name)) else : ( 'Осталось {0:d} работающих роботов.' format( \ (продолжение наследующей странице. Переменные класса и объекта A Byte of Python (Russian), Версия продолжение с предыдущей страницы Приветствие робота. Да, они это могут.''' ( 'Приветствую! Мои хозяева называют меня Выводит численность роботов.''' ( 'У нас {0:d} роботов.' format(Robot population)) howMany = staticmethod (howMany) droid1 = Robot( 'R2-D2' ) droid1 sayHi() Robot howMany() droid2 = Robot( 'C-3PO' ) droid2 sayHi() Robot Здесь роботы могут проделать какую-то работу.\n" ) ( "Роботы закончили свою работу. Давайте уничтожим их Вывод python3 Инициализация Приветствую Мои хозяева называют меня У нас 1 роботов. (Инициализация Приветствую Мои хозяева называют меня У нас 2 роботов. Здесь роботы могут проделать какую-то работу. Роботы закончили свою работу. Давайте уничтожим их уничтожается! Осталось 1 работающих роботов. (продолжение наследующей странице. Переменные класса и объекта A Byte of Python (Russian), Версия продолжение с предыдущей страницы уничтожается был последним. У нас 0 роботов. Как это работает: Это длинный примерно он помогает продемонстрировать природу переменных класса и объекта. Здесь population принадлежит классу, и поэтому является переменной класса. Переменная name принадлежит объекту (ей присваивается значение при помощи self ), и поэтому является переменной объекта. Таким образом, мы обращаемся к переменной класса population как, а не self.population . К переменной же объекта name во всех методах этого объекта мы обращаемся при помощи обозначения Помните об этой простой разнице между переменными класса и объекта. Также имейте ввиду, что переменная объекта стем же именем, что и переменная класса, сделает недоступной (спрячет) переменную класса! Метод howMany принадлежит классу, а не объекту. Это означает, что мы можем определить его как classmethod или staticmethod , в зависимости оттого, нужно ли нам знать, в каком классе мы находимся. Поскольку нам ненужна такая информация, мы воспользуемся Мы могли достичь того же самого, используя декораторы : @staticmethod def howMany (): '''Выводит численность роботов.''' ( 'У нас {0:d} роботов.' format(Robot Декораторы можно считать неким упрощённым способом вызова явного оператора, как мы видели в этом примере. Пронаблюдайте, как метод __init__ используется для инициализации экземпляра с именем. В этом методе мы увеличиваем счётчик population на 1, так как добавляем ещё одного робота. Также заметьте, что значения self.name для каждого объекта свои, что указывает на природу переменных объекта. Помните, что к переменными методам самого объекта нужно обращаться, пользуясь только self . Это называется доступом к атрибутам. В этом примере мы также наблюдали применение строк документации для классов, равно как и для методов. Вовремя выполнения мы можем обращаться к строке документации класса при помощи « Robot.__doc__ », а к строке документации метода – при помощи « Robot.sayHi.__doc__ ». Наряду с методом, существует и другой специальный метод, который. Переменные класса и объекта A Byte of Python (Russian), Версия вызывается тогда, когда объект собирается умереть, те. когда он больше не используется, и занимаемая им память возвращается операционной системе для другого использования. В этом методе мы просто уменьшаем счётчик Robot.population на 1. Метод __del__ запускается лишь тогда, когда объект перестаёт использоваться, а поэтому заранее неизвестно, когда именно этот момент наступит. Чтобы увидеть его в действии явно, придётся воспользоваться оператором del , что мы и сделали выше. Примечание для программистов на В Python все члены класса (включая данные) являются публичными (public), а все методы виртуальными Исключение Если имя переменной начинается с двойного подчёркивания, как, например, Python делает эту переменную приватной (private). Поэтому принято имя любой переменной, которая должна использоваться только внутри класса или объекта, начинать с подчёркивания; все же остальные имена являются публичными, и могут использоваться в других классах/объектах. Помните, что это лишь традиция, и Python вовсе не обязывает делать именно так (кроме двойного подчёркивания). 14.6 Наследование Одно из главных достоинств объектно-ориентированного программирования заключается в многократном использовании одного итого же кода, и один из способов этого достичь – при помощи механизма наследования. Легче всего представить себе наследование в виде отношения между классами как тип и подтип. Представим, что нам нужно написать программу, которая отслеживает информацию о преподавателях и студентах в колледже. У них есть некоторые общие характеристики: имя, возрасти адрес. Есть также и специфические характеристики, такие как зарплата, курсы и отпуск для преподавателей, а также оценки и оплата за обучение для студентов. Можно создать для них независимые классы и работать сними, но тогда добавление какой-либо новой общей характеристики потребует добавления её к каждому из этих независимых классов в отдельности, что делает программу неповоротливой. Лучше создать общий класс с именем, а затем сделать так, чтобы классы преподавателя и студента наследовали этот класс, те. чтобы они стали подтипами этого типа (класса, после чего добавить любые специфические характеристики к этим подти- пам. У такого подхода есть множество достоинств. Если мы добавим/изменим какую-либо функциональность в, это автоматически отобразится и во всех подтипах. Например, мы можем добавить новое поле удостоверения для преподавателей и студентов, просто добавив его к классу. С другой стороны, изменения в подтипах. Наследование A Byte of Python (Russian), Версия никак не влияют на другие подтипы. Ещё одно достоинство состоит в том, что обращаться к объекту преподавателя или студента можно как к объекту, что может быть полезно в ряде случаев, например, для подсчёта количества человек в школе. Когда подтип может быть подставлен в любом месте, где ожидается родительский тип, т.е. объект считается экземпляром родительского класса, это называется полиморфизмом. Заметьте также, что код родительского класса используется многократно, и нет необходимости копировать его вовсе классы, как пришлось бы в случае использования независимых классов. Класс SchoolMember в этой ситуации называют базовым классом или надклассом 3 . Классы и называют производными классами или подклассами 4 Рассмотрим теперь этот пример в виде программы (сохраните как Представляет любого человека в школе, name, age): self name = name self age = age Создан SchoolMember: {0} )' format( self Вывести информацию.''' ( 'Имя:" {0} " Возраст name, self age), end = " "Представляет преподавателя, name, age, salary): SchoolMember __init__ ( self , name, age) self salary = salary Создан Teacher: {0} )' format( self name)) def tell ( self ): SchoolMember Зарплата " {0:d} "' format( self Представляет студента, name, age, marks): SchoolMember __init__ ( self , name, age) self marks = marks Создан Student: {0} )' format( self name)) def tell ( self ): SchoolMember продолжение наследующей странице) 3 также «суперкласс», родительский класс (прим.перев.) 4 также «субкласс», «класс-наследник» (прим.перев.) 14.6. Наследование A Byte of Python (Russian), Версия продолжение с предыдущей страницы) ( 'Оценки: " {0:d} "' format( self marks)) t = Teacher( 'Mrs. Shrividya' , 40 , 30000 ) s = Student( 'Swaroop' , 25 , 75 ) () # печатает пустую строку, s] for member in members: member tell() # работает как для преподавателя, таки для студента Вывод: $ python3 Создан SchoolMember: Mrs. Создан Teacher: Mrs. Создан SchoolMember: Создан Student: Имя. Shrividya" Возраст" Зарплата "Имя" Возраст" Оценки "Как это работает: Чтобы воспользоваться наследованием, при определении класса мы указываем имена его базовых классов в виде кортежа, следующего сразу за его именем. Далее мы видим, что метод __init__ базового класса вызывается явно при помощи переменной self , чтобы инициализировать часть объекта, относящуюся к базовому классу. Это очень важно запомнить поскольку мы определяем метод __init__ в подклассах и, Python не вызывает конструктор базового класса автоматически – его необходимо вызывать самостоятельно в явном виде. Напротив, если мы неопределим метод __init__ в подклассе, Python вызовет конструктор базового класса автоматически. Здесь же мы видим, как можно вызывать методы базового класса, предваряя запись имени метода именем класса, а затем передавая переменную self вместе с другими аргументами. Обратите внимание, что при вызове метода tell из класса экземпляры или можно использовать как экземпляры SchoolMember Заметьте также, что вызывается метод tell из подкласса, а не метод tell из класса. Это можно понять следующим образом Python всегда начинает поиск методов в самом классе, что они делает в данном случае. Если. Наследование A Byte of Python (Russian), Версия же он не находит метода, он начинает искать методы, принадлежащие базовым классам по очереди, в порядке, в котором они перечислены в кортеже при определении класса. Замечание по терминологии если при наследовании перечислено более одного класса, это называется множественным наследованием. Параметр end используется в методе для того, чтобы новая строка начиналась через пробел после вызова print() 14.7 Метаклассы 5 В обширной теме объектно-ориентированного программирования существует ещё много всего, номы лишь слегка коснёмся некоторых концепций, чтобы вы просто знали об их существовании. Точно также, как классы используются для создания объектов, можно использовать ме- таклассы для создания классов. Метаклассы существуют для изменения или добавления нового поведения в классы. Давайте рассмотрим пример. Допустим, мы хотим быть уверены, что мы всегда создаём исключительно экземпляры подклассов класса, и не создаём экземпляры самого класса SchoolMember Для достижения этой цели мы можем использовать концепцию под названием абстрактные базовые классы. Это означает, что такой класс абстрактен, те. является лишь некой концепцией, не предназначенной для использования в качестве реального класса. Мы можем объявить наш класс как абстрактный базовый класс при помощи встроенного метакласса по имени python # Filename: Представляет любого человека в школе, name, age): self name = name self age = age Создан SchoolMember: {0} )' format( self продолжение наследующей странице) 5 в оригинальной версии книги этот параграф невидим для читателей, так как находится в комментарии с пометкой автора «It is too sudden to introduce this concept here.», что означает Слишком неожиданно представление этой концепции здесь (прим.перев.) 14.7. Метаклассы 5 118 A Byte of Python (Russian), Версия продолжение с предыдущей страницы) @abstractmethod def tell ( self ): '''Вывести информацию.''' ( 'Имя:" {0} " Возраст name, self age), end = " "Представляет преподавателя, name, age, salary): SchoolMember __init__ ( self , name, age) self salary = salary Создан Teacher: {0} )' format( self name)) def tell ( self ): SchoolMember Зарплата " {0:d} "' format( self Представляет студента, name, age, marks): SchoolMember __init__ ( self , name, age) self marks = marks Создан Student: {0} )' format( self name)) def tell ( self ): SchoolMember Оценки " {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" () # печатает пустую строку, s] for member in members: member tell() # работает как для преподавателя, таки для студента Вывод: $ python3 Создан SchoolMember: Mrs. Создан Teacher: Mrs. продолжение наследующей странице. Метаклассы 5 119 A Byte of Python (Russian), Версия продолжение с предыдущей страницы) (Создан SchoolMember: Создан Student: Имя. Shrividya" Возраст" Зарплата "Имя" Возраст" Оценки "Как это работает: Мы можем объявить метод tell класса абстрактными таким образом автоматически запретим создавать экземпляры класса SchoolMember Тем не менее, мы можем работать с экземплярами итак, как будто они экземпляры, поскольку они являются подклассами Резюме Мы изучили различные аспекты классов и объектов, равно как и терминологию, связанную сними. Мы также увидели ряд достоинств и подводных камней» объектно-ориентированного программирования. Python – в высокой степени объектно- ориентирован, поэтому понимание этих принципов очень поможет вам в дальнейшем. Далее мы узнаем, как работать с вводом/выводом и получать доступ к файлам в Python. 14.8. Резюме Ввод-вывод Рано или поздно возникают ситуации, когда программа должна взаимодействовать с пользователем. Например, принять какие-нибудь данные от пользователя, а затем вывести результаты. Для этого применяются функции и print() соответственно. Для вывода можно также использовать различные методы класса строка. К примеру, при помощи метода rjust можно получить строку, выравненную по правому краю к указанной ширине. См. help(str) для более подробного описания. Ещё одним распространённым типом ввода/вывода является работа с файлами. Возможность создавать, читать и записывать в файлы является ключевой для многих программ, поэтому в настоящей главе и мы рассмотрим этот аспект Ввод от пользователя Сохраните эту программу как Введите текст 'Да, это палиндром" ) else : ( "Нет, это не палиндром" ) Вывод: $ python3 Введите текст сэр Нет, это не палиндром (продолжение наследующей странице A Byte of Python (Russian), Версия продолжение с предыдущей страницы python3 Введите текст мадам Да, это палиндром python3 Введите текст топот Да, это палиндром Как это работает: Мы применяем операцию вырезки для переворачивания текста. Мы уже видели, как создаются вырезки из последовательностей при помощи кода, начиная с позиции до позиции b . Но ведьмы также можем указать и третий аргумент, определяющий шаг, с которым производится вырезка. По умолчанию шаг равен, поэтому и возвращается непрерывный фрагмент текста. Указание отрицательного шага, т.е. -1 приведёт к выводу текста в обратном порядке. Функция принимает строку в качестве аргумента и показываете пользователю. Затем она ждёт, чтобы пользователь набрал что-нибудь и нажал клавишу ввода. Как только пользователь это сделал, функция возвращает введённый пользователем текст. Мы считываем этот текст и выстраиваем его в обратном порядке. Если пе- ревёрнутый и исходный текст одинаковы, значит введённый текст является палиндромом |