Математический анализ. 3е издание
Скачать 4.86 Mb.
|
class rec: pass # Объект пустого пространства имен Инструкция пустой операции pass (обсуждавшаяся в главе 13) необхо дима потому, что здесь у нас нет никаких методов с программным ко дом. После создания класса, выполнив инструкцию в интерактивной оболочке, мы можем приступать к присоединению атрибутов, выпол няя операции присваивания изза пределов класса: >>> rec.name = 'Bob' # Так же для объектов с атрибутами >>> rec.age = 40 1 Но лучше этого не делать. Обычная практика показывает, что перегружен ные операторы должны работать так же, как и встроенные реализации. В данном случае это означает, что метод __mul__ должен был бы возвращать новый объект в виде результата, а не изменять существующий экземпляр (self); для непосредственного изменения объекта лучше предусмотреть метод mul , чем выполнять перегрузку оператора * (например, mul(3), вместо a*3). 590 Глава 23. Основы программирования классов После того как атрибуты будут созданы, мы можем обращаться к ним с помощью обычного синтаксиса. Когда класс используется таким спо собом, он напоминает структуры в языке C или записи в языке Pascal – объект с присоединенными к нему полями (то же самое можно проде лывать с ключами словарей, но для этого потребуется вводить допол нительные символы): >>> print rec.name # Как структура в языке C или запись Bob Обратите внимание: такой подход будет работать даже в случае, когда еще не было создано ни одного экземпляра класса. Классы – это полно ценные объекты, даже если нет ни одного экземпляра. Фактически они всего лишь самостоятельные пространства имен, поэтому, пока у нас имеется ссылка на класс, мы можем в любое время добавлять или изменять его атрибуты по своему усмотрению. Однако посмотрим, что произойдет, когда будут созданы два экземпляра класса: >>> x = rec() # Экземпляры наследуют имена из класса >>> y = rec() Эти экземпляры начинают свое существование как объекты абсолют но пустых пространств имен. Однако изза того, что они помнят класс, из которого были созданы, они по наследству получат атрибуты, кото рые мы присоединили к классу: >>> x.name, y.name # Сейчас имена хранятся только в классе ('Bob', 'Bob') В действительности у этих экземпляров нет своих собственных атри бутов – они просто получают атрибут name из класса. Тем не менее если выполнить присваивание атрибуту экземпляра, будет создан (или из менен) атрибут этого объекта, а не другого – атрибуты обнаруживают ся в результате поиска по дереву наследования, но операция присваи вания значения атрибуту воздействует только на тот объект, к которо му эта операция применяется. Ниже экземпляр x получает свой собст венный атрибут name, а экземпляр y попрежнему наследует атрибут name , присоединенный к классу выше его: >>> x.name = 'Sue' # Но присваивание изменит только объект x >>> rec.name, x.name, y.name ('Bob', 'Sue', 'Bob') Фактически, как будет более подробно рассказано в следующей главе, атрибуты объекта пространства имен обычно реализованы в виде сло варей, и деревья наследования классов (вообще говоря) тоже всего лишь словари со ссылками на другие словари. Если знать, куда смот реть, в этом можно убедиться явно. В большинстве объектов, создан ных на базе классов, имеется атрибут __dict__, который является сло варем пространства имен: >>> rec.__dict__.keys() ['age', '__module__', '__doc__', 'name'] Самый простой в мире класс на языке Python 591 >>> x.__dict__.keys() ['name'] >>> y.__dict__.keys() [] Здесь в словаре класса присутствуют атрибуты name и age, которые мы создали ранее, объект x имеет свой собственный атрибут name, а объект y попрежнему пуст. Каждый экземпляр имеет ссылку на свой наследуе мый класс, она называется __class__, если вам захочется проверить ее: >>> x.__class__ Классы также имеют атрибут __base__, который представляет собой кортеж его суперклассов – эти два атрибута описывают, как деревья классов размещаются в памяти. Главное, что следует из этого взгляда на внутреннее устройство, это то, что модель классов в языке Python чрезвычайно динамична. Классы и экземпляры – это всего лишь объекты пространств имен с атрибута ми, создаваемыми на лету с помощью операции присваивания. Обыч но эти операции присваивания выполняются внутри инструкции class , но они могут находиться в любом другом месте, где имеется ссылка на один из объектов в дереве. Даже методы, которые обычно создаются инструкциями def, вложен ными в инструкцию class, могут создаваться совершенно независимо от объекта класса. Например, ниже определяется простая функция вне какоголибо класса, которая принимает единственный аргумент: >>> def upperName(self): ... return self.name.upper() # Аргумент self попрежнему необходим Здесь еще ничего не говорится о классе – это простая функция, и она может вызываться как обычная функция при условии, что объект, по лучаемый ею, имеет атрибут name (в данном случае имя аргумента self не имеет никакого особого смысла). Однако если эту простую функцию присвоить атрибуту нашего класса, она станет методом, вызываемым из любого экземпляра (а также через имя самого класса при условии, что функции вручную будет передан экземпляр): 1 1 Фактически это одна из причин, почему в языке Python аргумент self все гда должен явно объявляться в методах, – потому что методы могут созда ваться как простые функции, независимо от классов, и им необходим яв ный аргумент со ссылкой на подразумеваемый экземпляр. В противном случае интерпретатор не смог бы обеспечить превращение простой функции в метод класса. Однако основная причина, по которой аргумент self объяв ляется явно, заключается в том, чтобы сделать назначение имен более оче видным. Имена, к которым обращаются не через аргумент self, являются простыми переменными, тогда как имена, обращение к которым происхо дит через аргумент self, очевидно являются атрибутами экземпляра. 592 Глава 23. Основы программирования классов >>> rec.method = upperName >>> x.method() # Вызвать метод для обработки x 'SUE' >>> y.method() # То же самое, но в self передается y 'BOB' >>> rec.method(x) # Можно вызвать через имя экземпляра или класса 'SUE' Обычно заполнение классов производится внутри инструкции class, а атрибуты экземпляров создаются в результате присваивания значе ний атрибутам аргумента self в методах. Однако отметим снова, что все это не является обязательным, поскольку ООП в языке Python – это в основном поиск атрибутов во взаимосвязанных объектах про странств имен. В заключение В этой главе были представлены основы программирования классов в языке Python. Мы изучили синтаксис инструкции class и увидели, как ее можно использовать для построения дерева классов. Мы также узнали, что интерпретатор Python автоматически заполняет первый аргумент методов, что атрибуты присоединяются к объектам в дереве классов простым присваиванием и что существуют специальные име на методов перегрузки операторов, позволяющие реализовать выпол нение встроенных операций в наших объектах (например, операции выражений и вывод). В следующей главе мы продолжим изучение программирования клас сов и повторно рассмотрим модель, чтобы восполнить недостающие подробности, которые были опущены здесь для простоты. Мы также приступим к исследованию нескольких больших классов, обладаю щих большей практической ценностью. Однако для начала ответьте на контрольные вопросы, чтобы освежить в памяти основы, которые бы ли рассмотрены здесь. Закрепление пройденного Контрольные вопросы 1. Как классы связаны с модулями? 2. Как создаются классы и экземпляры классов? 3. Где и как создаются атрибуты классов? 4. Где и как создаются атрибуты экземпляров классов? 5. Что в языке Python означает слово self для классов? Закрепление пройденного 593 6. Как производится перегрузка операторов в классах на языке Py thon? 7. Когда может потребоваться перегрузка операторов в ваших клас сах? 8. Какой метод перегрузки оператора используется наиболее часто? 9. Какие две концепции ООП являются наиболее важными в языке Python? Ответы 1. Классы всегда находятся внутри модулей – они являются атрибута ми объекта модуля. Классы и модули являются пространствами имен, но классы соответствуют инструкциям (а не целым файлам) и поддерживают такие понятия ООП, как экземпляры класса, на следование и перегрузка операторов. В некотором смысле модули напоминают классы с единственным экземпляром, без наследова ния, которые соответствуют целым файлам. 2. Классы создаются с помощью инструкций class, а экземпляры соз даются вызовом класса, как если бы это была функция. 3. Атрибуты класса создаются присваиванием атрибутам объекта класса. Обычно они создаются инструкциями верхнего уровня в ин струкции class – каждое имя, которому будет присвоено значение внутри инструкции class, становится атрибутом объекта класса (с технической точки зрения область видимости инструкции class преобразуется в пространство имен атрибутов объекта класса). Ат рибуты класса могут также создаваться через присваивание атри бутам класса в любом месте, где доступна ссылка на объект класса, то есть даже за пределами инструкции class. 4. Атрибуты экземпляра создаются присваиванием атрибутам объек та экземпляра. Обычно они создаются внутри методов класса, в ин струкции class – присваиванием значений атрибутам аргумента self (который всегда является подразумеваемым экземпляром). По вторюсь: возможно создавать атрибуты с помощью операции при сваивания в любом месте программы, где доступна ссылка на экзем пляр, даже за пределами инструкции class. Обычно все атрибуты эк земпляров инициализируются в конструкторе __init__ – благодаря этому при последующих вызовах методов можно быть уверенным в существовании необходимых атрибутов. 5. self – это имя, которое обычно дается первому (самому левому) ар гументу в методах классов. Интерпретатор Python автоматически записывает в него объект экземпляра, который подразумевается при вызове метода. Этот аргумент не обязательно должен носить имя self, главное – это положение аргумента. (Бывшие программи сты на C++ или Java могут предпочесть использовать имя this, по тому что в этих языках программирования это имя является отра 594 Глава 23. Основы программирования классов жением той же идеи, только в языке Python этот аргумент всегда должен присутствовать явно.) 6. Перегрузка операторов в языке Python выполняется с помощью ме тодов со специальными именами – они начинаются и заканчивают ся двумя символами подчеркивания. Эти имена не являются встро енными или зарезервированными именами – интерпретатор Python просто автоматически вызывает методы с этими именами, когда эк земпляр появляется в составе соответствующей операции. Язык Python определяет порядок отображения операций на специальные имена методов. 7. Перегрузка операторов может использоваться для реализации объ ектов, которые имитируют поведение встроенных типов (напри мер, последовательностей или числовых объектов, таких как мат рицы), и для реализации интерфейсов встроенных типов, которые ожидают получить те или иные части программного кода. Кроме того, имитация интерфейсов встроенных типов позволяет переда вать в экземплярах классов информацию о состоянии – то есть ат рибуты, в которых сохраняются данные между вызовами опера ций. Однако не следует использовать перегрузку операторов, когда достаточно использовать простые методы. 8. Наиболее часто используется метод конструктора __init__ – этот метод присутствует практически во всех классах и используется для установки начальных значений атрибутов экземпляров и вы полнения других начальных операций. 9. Наиболее важными концепциями ООП в языке Python являются аргумент self в методах и конструктор __init__. 24 Подробнее о программировании классов Если чтото в главе 23 осталось для вас непонятным, не волнуйтесь – те перь, совершив краткий тур, мы начнем копать немного глубже и под робно изучим понятия, представленные ранее. В этой главе мы с другой стороны посмотрим на классы и методы, наследование и перегрузку операторов, формализуем и дополним некоторые идеи программирова ния классов, представленные в главе 23. Поскольку классы являются нашим последним инструментом пространств имен, то здесь мы также сведем вместе концепции пространств имен в языке Python. Кроме то го, в этой главе будут представлены примеры более крупных и более реалистичных классов, чем те, что мы видели до сих пор, в том числе завершающий пример, который связывает воедино многое из того, что мы узнали об ООП. Инструкция class Несмотря на то что на первый взгляд инструкция class в языке Python напоминает похожие инструменты в других языках программирова ния, при более близком рассмотрении видно, что она существенно от личается от того, к чему привыкли некоторые программисты. Напри мер, как и инструкция class в языке C++, инструкция class в языке Python является основным инструментом ООП, но в отличие от инст рукции в C++, в языке Python она не является объявлением. Подобно инструкции def, инструкция class создает объект и является неявной инструкцией присваивания – когда она выполняется, создается объ ект класса, ссылка на который сохраняется в имени, использованном в заголовке инструкции. Кроме того, как и инструкция def, инструк ция class является настоящим выполняемым программным кодом – класс не существует, пока поток выполнения не достигнет инструкции 596 Глава 24. Подробнее о программировании классов class , которая определяет его (обычно при импортировании модуля, в котором она находится, но не ранее). Общая форма Инструкция class – это составная инструкция, с блоком операторов, обычно под строкой заголовка. В заголовке после имени в круглых скобках через запятую перечисляются суперклассы. Наличие более одного суперкласса в списке означает множественное наследование (которое будет обсуждаться в следующей главе). Ниже показана об щая форма инструкции: class data = value # Совместно используемые данные класса def method(self,...): # Методы self.member = value # Данные экземпляров Внутри инструкции class любая операция присваивания создает атри бут класса, а методы со специальными именами перегружают операто ры. Например, функция с именем __init__, если она определена, вызы вается во время создания объекта экземпляра. Пример Как мы уже видели, классы – это всего лишь пространства имен, то есть инструменты, определяющие имена (атрибуты), с помощью кото рых клиентам экспортируются данные и логика. Так как же инструк ция class порождает пространство имен? А вот как. Так же как и в модулях, инструкции, вложенные в тело ин струкции class, создают атрибуты класса. Когда интерпретатор дости гает инструкции class (а не тогда, когда происходит вызов класса), он выполняет все инструкции в ее теле от начала и до конца. Все присваи вания, которые производятся в течение этого процесса, создают имена в локальной области видимости класса, которые становятся атрибу тами объекта класса. Благодаря этому классы напоминают модули и функции: • Подобно функциям, инструкции class являются локальными об ластями видимости, где располагаются имена, созданные вложен ными операциями присваивания. • Подобно именам в модуле, имена, созданные внутри инструкции class , становятся атрибутами объекта класса. Основное отличие классов состоит в том, что их пространства имен также составляют основу механизма наследования в языке Python – ссылки на атрибут, отсутствующие в классе или в объекте экземпляра, будут получены из других классов. Поскольку инструкция class – это составная инструкция, в ее тело мо гут быть вложены любые инструкции – print, =, if, def и т. д. Все инст Инструкция class 597 рукции внутри инструкции class выполняются, когда выполняется са ма инструкция class (а не когда позднее класс вызывается для созда ния экземпляра). Операции присваивания именам внутри инструкции class создают атрибуты класса, а вложенные инструкции def создают методы класса; кроме этого, атрибуты класса создаются и другими ин струкциями, выполняющими присваивание. Например, присваивание объекта, не являющегося функцией, атрибу там создает атрибуты данных, совместно используемых всеми экзем плярами: >>> class SharedData: ... spam = 42 # Создает атрибут данных класса >>> x = SharedData() # Создать два экземпляра >>> y = SharedData() >>> x.spam, y.spam # Они наследуют и совместно используют # атрибут spam (42, 42) В данном случае изза того, что имя spam создается на верхнем уровне в инструкции class, оно присоединяется к классу и поэтому совместно используется всеми экземплярами. Мы можем изменять значение ат рибута, выполняя присваивание через имя класса и обращаться к не му через имена экземпляров или класса. 1 >>> >>> x.spam, y.spam, SharedData.spam (99, 99, 99) Такие атрибуты класса могут использоваться для хранения информа ции, доступной всем экземплярам, например для хранения счетчика количества созданных экземпляров (эту идею мы рассмотрим в главе 26). Теперь посмотрим, что произойдет, если присвоить значение ат рибуту spam не через имя класса, а через имя экземпляра: >>> x.spam = 88 >>> x.spam, y.spam, SharedData.spam (88, 99, 99) Операция присваивания, применяемая к атрибуту экземпляра, созда ет или изменяет имя в экземпляре, а не в классе. Вообще говоря, поиск в дереве наследования производится только при попытке чтения ат рибута, но не при присваивании: операция присваивания атрибуту 1 Если у вас есть опыт работы с языком C++, вы можете заметить в этом не которое сходство со «статическими» членами данных в языке С++ – члена ми, которые хранятся в классе независимо от экземпляров. В языке Python в этом нет ничего особенного: все атрибуты класса – это всего лишь имена, созданные в инструкции class, независимо от того, ссылаются они на мето ды («методы» в C++) или на чтото другое («члены» в C++). |