Как устроен Python. Как устроен Python. Харрисон. Харрисон Мэтт
Скачать 5.41 Mb.
|
193 Если программисты начнут обсуждать b , вы можете услышать самые разные термины. « b — это строка», « b — это объект», « b — это экземпляр строки»… Пожалуй, последняя формулировка самая конкретная. Однако b при этом не является классом строки. Классы status = "off" Код "off" Id:2e6a Type:String Что делает компьютер Переменные Объекты status str Id:1aea Type:Type Рис. 21.1. Объект строки. У каждого объекта имеется тип, которым в действительности является класс объекта. В данном случае это класс str ПРИМЕЧАНИЕ Класс str также может использоваться для создания строк, но чаще исполь- зуется для преобразования типа. Строки встроены в язык, поэтому передавать строковый литерал классу str было бы лишним. Иначе говоря, не нужно использовать запись >>> c = str("I'm a string"), так как Python автоматически создает строку при заключении символов в ка- вычки. Термин «литерал» в данном случае означает специальный синтаксис создания строк, встроенный в Python. С другой стороны, если у вас имеется число, которое нужно преобразовать в строку, можно вызвать str : 194 Глава 21. Классы >>> num = 42 >>> answer = str(num) >>> answer '42' Говорят, что Python «поставляется с батарейками» — вместе с библиоте- ками и классами, заранее определенными для разработчика. Эти классы обычно получаются достаточно универсальными. Вы можете определять собственные классы, специализированные для вашей предметной обла- сти, специализированные объекты с состоянием и логику для изменения этого состояния. Классы status = "off" "off" Id:2e6a __class__:str status Id:1aea __class__:type __call__ Id:1be2 __class__:method_ capitalize descriptor Класс строк и Ме то д строк и Код Что делает компьютер Переменные Объекты Рис. 21.2. Обновленная версия объекта строки. Тип изменился на __class__, потому что при анализе объекта атрибут __class__ указывает на класс объекта. Этот класс содержит разные методы (на схеме показан только метод capitalize, но есть много других). Методы тоже являются объектами, как видно из диаграммы 21.1. Планирование класса 195 21.1. Планирование класса Прежде всего заметим, что классы не всегда необходимы в Python. По- думайте, нужно ли определять класс или же хватит функции (или группы функций). Класс может быть полезен для программного представления физических или концептуальных объектов. Понятия, являющиеся опи- саниями, — скорость, температура, среднее арифметическое, цвет — не являются хорошими кандидатами для классов. Если вы решили, что хотите моделировать что-либо с помощью класса, задайте себе следующие вопросы: У него есть имя? Какими свойствами он обладает? Присущи ли эти свойства всем экземплярам класса? А именно: • Какие свойства являются общими для класса в целом? • Какие из этих свойств уникальны для каждого экземпляра? Какие операции он выполняет? Рассмотрим конкретный пример. Предположим, вы работаете на горно- лыжном курорте и хотите моделировать использование подъемников. Один из вариантов основан на создании класса, определяющего кресло на подъемник. Подъемник (если вдруг вы никогда им не пользовались) оснащается множеством кресел. Лыжники встают в очередь у основания горы, чтобы сесть на кресла, а затем сходят с них на вершине. Разумеется, модель необходимо до определенного уровня абстрагиро- вать. Не нужно моделировать каждое низкоуровневое свойство кресла. Например, для расчета нагрузки неважно, из какого материала сделано кресло — из алюминия, стали или дерева. С другой стороны, это может быть важно для некоторых лыжников. Есть ли имя у моделируемой сущности? Да, «кресло». Среди свойств кресла можно выделить номер, вместимость, наличие планки безопас- ности и мягких сидений. Если погрузиться немного глубже, вместимость можно разбить на максимальную вместимость и текущую занятость. Максимальная емкость должна оставаться постоянной, тогда как заня- тость может изменяться в любой момент времени. 196 Глава 21. Классы С креслом связан ряд операций — например, добавление людей у под- ножия горы и выход на вершине. Другим действием может стать пози- ция планки безопасности. Мы будем обрабатывать загрузку и выгрузку посетителей, но в нашей модели позиция планки безопасности будет игнорироваться. 21.2. Определение класса Ниже приведен класс Python, представляющий кресло на подъемнике. Сначала рассмотрим простое определение класса; комментарии прону- мерованы для последующего обсуждения: >>> class Chair: # 1 ... ''' A Chair on a chairlift ''' # 2 ... max_occupants = 4 # 3 ... def __init__(self, id): # 4 ... self.id = id # 5 ... self.count = 0 ... def load(self, number): # 6 ... self.count += number ... def unload(self, number): # 7 ... self.count -= number Ключевое слово class в Python определяет класс. Классу должно быть присвоено имя (1), за которым следует двоеточие. Вспомните, что в Python за двоеточием следует блок с отступом (кроме использования в срезах). Также обратите внимание на последовательные отступы под определением класса и на то, что имя класса начинается с прописной буквы. ПРИМЕЧАНИЕ Имена классов записываются в «верблюжьем регистре». Так как Chair — одно слово, вы могли этого и не заметить. В отличие от функций, в именах которых слова соединяются символами подчеркивания, в «верблюжьем регистре» каждое слово начинается с прописной буквы, а сами слова просто соединя- ются без разделителей. Обычно в качестве имен классов используются имена 21.2. Определение класса 197 существительные. В Python имена классов не могут начинаться с цифр. Ниже приведены примеры имен классов — как хороших, так и плохих. • Kitten # хорошо • jaguar # плохо - начинается со строчной • SnowLeopard # хорошо - "верблюжий регистр" • White_Tiger # плохо - содержит подчеркивания • 9Lives # плохо - начинается с цифры За дополнительной информацией о выборе имен классов обращайтесь к PEP 8. Следует заметить, что многие встроенные типы не соблюдают это правило: str , int , float и т. д. Непосредственно после объявления класса можно вставить строку до- кументации (2). Это самая обычная строка. Обратите внимание: если эта строка заключена в тройные кавычки, она может состоять из нескольких абзацев. Строки документации не обязательны, но они могут пригодиться читателям вашего кода; кроме того, они выводятся функцией help при анализе кода в REPL. Используйте строки документации осмотрительно, и она принесет огромную пользу. Внутри тела класса, снабженного отступами, можно создать атрибуты класса (3). Атрибут класса используется для хранения состояния, общего для всех экземпляров класса. В нашем примере любое кресло, которое мы будем создавать, может вмещать до четырех посетителей. У атрибутов классов есть свои преимущества. Так как значение задается на уровне класса, вам не придется повторяться и задавать его при создании каждого нового кресла. С другой стороны, ваши кресла будут жестко запрограм- мированы так, чтобы они поддерживали только четыре места. Позднее вы узнаете, как переопределить атрибут класса. Затем следует команда def (4). Все выглядит так, словно мы определяем функцию внутри тела класса. Все верно, если не считать того, что функ- ция, определяемая прямо в теле класса, называется методом. Так как этот метод имеет специальное имя __init__ , он называется конструктором. Метод получает два параметра, self и id . У большинства методов первым параметром является self . Его можно интерпретировать как экземпляр класса. 198 Глава 21. Классы Конструктор вызывается при создании экземпляра класса. Если рас- сматривать класс как «фабрику», которая предоставляет шаблон или чертеж для создания экземпляров, то конструктор инициализирует со- стояние этих экземпляров. Конструктор получает экземпляр во входных данных (параметр self ) и обновляет его внутри метода. Python помогает вам и передает экземпляр автоматически. Впрочем, это может породить путаницу, но об этом позднее. Внутри тела конструктора (5) (оно снабжено отступом, потому что следу- ет за двоеточием) присоединяются два атрибута, которые будут уникаль- ными для экземпляра: id и count . На большинстве подъемников каждое кресло помечается уникальным номером. Атрибут id представляет число. Кроме того, на одном кресле могут ехать несколько лыжников — их коли- чество хранится в атрибуте count и инициализируется нулем. Обратите внимание: конструктор ничего не возвращает, но обновляет значения, уникальные для экземпляра. ПРИМЕЧАНИЕ В Python есть встроенная функция id , но вы также можете использовать это имя как имя атрибута класса. Функция id при этом остается доступной. Каждый раз, когда вы хотите обратиться к атрибуту id , проводится поиск по экземпляру. Если экземпляру было присвоено имя chair , то для полу- чения значения id следует использовать запись chair.id . Таким образом, встроенная функция не замещается. О завершении логики конструктора можно судить по исчезновению уровня отступа. Мы видим определение другого метода (6), load . Этот метод представляет операцию, которая может выполняться экземпляром класса. В данном случае кресло может загружать пассажиров, а этот метод сообщает экземпляру, что нужно делать в подобных ситуациях. И снова self (экземпляр) является первым параметром метода. Второй параметр, number , содержит количество людей, садящихся на кресло. Помните, что кресло на подъемнике обычно вмещает несколько (до четырех в нашем примере) человек. Когда лыжник садится на кресло, нужно вызвать ме- тод load для кресла, а внутри тела этого метода обновить атрибут count экземпляра. Также существует парный метод unload (7), который должен вызываться при сходе лыжника с подъемника на вершине горы. 21.2. Определение класса 199 Классы, определяемые пользователем class Chair: '''Another Chair''' max_occupants = 4 def __init__(self, id): self.id = id self.count = 0 def load(self, number): self.count += number def unload(self, number): self.count -= number Код Что делает компьютер Переменные Объекты Chair Id:1aea __class__:type 4 Id:1be2 __class__:int max_occupants __init__ load Id:2a00 __class__:function Id:2aa4 __class__:function Рис. 21.3. Создание экземпляра класса. Python создает за вас новый тип. Все атрибуты класса или методы будут храниться в атрибутах нового класса. Атрибуты экземпляров (id и count) в классе отсутствуют, потому что они определяются для экземпляров ПРИМЕЧАНИЕ Не бойтесь методов. Вы уже видели многие методы — например, метод .capitalize , определенный для строки. Методы представляют собой функ- ции, присоединенные к классу. Метод вызывается не сам по себе, а для кон- кретного экземпляра класса: >>> 'matt'.capitalize() 'Matt' 200 Глава 21. Классы Таким образом, создать класс несложно. Сначала вы определяете нуж- ные атрибуты. Те атрибуты, которые существуют на уровне класса, раз- мещаются в определении класса. Атрибуты, уникальные для каждого экземпляра, размещаются в конструкторе. Вы также можете определить методы с операциями, изменяющими экземпляр класса. После того как класс будет определен, Python создает переменную с именем класса, которая указывает на класс: >>> Chair Для получения расширенной информации о классе можно воспользо- ваться функцией dir . Обратите внимание: атрибуты класса определяются в классе, и к ним можно обращаться с указанием имени класса: >>> dir(Chair) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'load', 'max_occupants', 'unload'] >>> Chair.max_occupants 4 На диаграмме в этом разделе показано, как хранятся атрибуты и методы класса. Также список атрибутов и методов можно просмотреть из REPL. Так как в Python нет ничего, кроме объектов, все они обладают атрибу- том __class__ : >>> Chair.__class__ >>> Chair.max_occupants.__class__ >>> Chair.__init__.__class__ >>> Chair.load.__class__ >>> Chair.unload.__class__ 21.3. Создание экземпляра класса 201 Методы также определяются на уровне класса, но атрибуты экземпля- ров — нет. Так как атрибуты экземпляров уникальны для объекта, они хранятся в экземпляре. Если в вашем классе или его методах определены строки документации, вы можете просмотреть их при помощи help : >>> help(Chair) Help on class Chair in module __main__: class Chair(builtins.object) | A Chair on a chairlift | | Methods defined here: | | __init__(self, id) | | load(self, number) | | unload(self, number) | | ------------------------------------------------------ | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) | | ------------------------------------------------------ | Data and other attributes defined here: | | max_occupants = 4 21.3. Создание экземпляра класса Итак, вы определили класс, моделирующий кресло на подъемнике, и мо- жете создавать экземпляры этого класса. Класс Chair можно сравнить с фаб рикой: он берет заготовки объектов и превращает их в объекты кресел. 202 Глава 21. Классы Если говорить конкретно, то эта задача решается методом конструкто- ра __init__ . В первом параметре передается минимальный объект self , то есть «заготовка». Python назначает объекту атрибут __class__ (ука- зывающий на класс Chair ) перед тем, как передавать его конструктору. Возможно, другая аналогия поможет вам лучше понять, как работают классы: вспомните, как в мультфильмах изображают рождение детей. Нерожденные дети обитают где-то в облаках. В один прекрасный момент прилетает аист, забирает ребенка с облака и доставляет его в колыбель в родительском доме. При вызове конструктора Python забирает ребен- ка с облака (получает объект). Он доставляет ребенка в дом, делает его членом семьи (присваивая атрибуту __class__ значение Chair или другое значение, соответствующее вашему классу). Когда ребенок окажется в доме, его можно будет одеть, помыть и т. д. Помните, что объекты хранят состояние, которое может изменяться при помощи операций. Ниже приведен код создания кресла с номером 21 на языке Python. При вызове класса (то есть указании имени класса с круглыми скобками) вы сообщаете Python о необходимости вызова конструктора. В отличие от некоторых языков, в Python не нужно использовать ключевое слово new или указывать тип; достаточно поставить круглые скобки с параметрами конструктора после имени класса: >>> chair = Chair(21) Еще раз уточним терминологию: переменная chair указывает на объект, то есть экземпляр. Она не указывает на класс. Объект относится к классу Chair . Экземпляр содержит ряд атрибутов, включая count и id Чтобы обратиться к атрибуту экземпляра, следует указать его экземпляр ( chair ): >>> chair.count 0 >>> chair.id 21 В Python используется определенная иерархия поиска атрибутов. Снача- ла Python ищет атрибут в экземпляре. Если поиск оказывается безуспеш- ным, Python переходит к поиску атрибута в классе (так как экземпляры знают, к какому классу они принадлежат). Если и на этот раз поиск 21.3. Создание экземпляра класса 203 завершится неудачей, Python выдает ошибку AttributeError (атрибут отсутствует). Атрибут max_occupants обычно хранится в классе, но к нему также можно обратиться через экземпляр: >>> chair.max_occupants 4 Во внутренней реализации Python заменяет это обращение следующим: >>> chair.__class__.max_occupants 4 Класс как фабрика chair = Chair(21) Код Что делает компьютер Переменные Объекты Chair Id:1aea __class__:type max_occupants loads Id:1ca8 __class__:function id count Id:1cb2 __class__:Chair chair 21 Int 0 Int 4 Int Рис. 21.4. Процесс построения объекта. При вызове конструктора «аист» — Python приносит конструктору «ребенка» — объект (self). У этого объекта установлен атрибут __class__, но конструктор может свободно изменять экземпляр, добавляя новые атрибуты. Объект превращается в chair 204 Глава 21. Классы Поиск атрибутов отличается от поиска переменных. Вспомните, что Python начинает поиск переменных с локальной области видимости, затем переходит к глобальной области видимости, затем к встроенной — и в итоге выдает ошибку NameError , если поиск не дал результатов. Поиск атрибутов начинается с экземпляра, затем переходит к классу, а если атрибут не найден, выдается ошибка AttributeError 21.4. Вызов метода Если у вас имеется экземпляр класса, для него можно вызвать методы. Методы, как и функции, вызываются с круглыми скобками, в которых перечисляются аргументы. В следующем примере вызывается метод .load , добавляющий трех лыжников на кресло: >>> chair.load(3) Кратко разберем синтаксис вызова метода. Сначала указывается экзем- пляр ( chair ), за которым следует точка. Точка в Python обозначает поиск атрибута (если только она не следует за числовым литералом). Когда вы видите, что за экземпляром следует точка, помните, что Python будет искать то, что идет после точки. Сначала Python ищет load в экземпляре. В экземпляре этот атрибут найти не удается (вспомните, что в конструкторе для экземпляра были назна- чены только атрибуты count и id ). Однако экземпляр также содержит ссылку на свой класс. Так как поиск в экземпляре завершился неудачей, Python переходит к поиску этих атрибутов в классе. Метод .load опре- делен для класса Chair , поэтому Python возвращает его. Круглые скобки обозначают вызов метода, а число 3 передается в параметре метода. Вспомните, как выглядело объявление load : ... def load(self, number): # 6 ... self.count += number В объявлении указаны два параметра, self и number , а при вызове пере- дается только один параметр 3. Почему количество параметров не со- впадает? Параметр self представляет экземпляр ( chair в данном случае). Python вызывает метод .load , передавая chair в параметре self и 3 в па- |