Главная страница
Навигация по странице:

  • Классы – это атрибуты в модулях

  • Z (экземпляр)

  • Самый простой в мире класс на языке Python

  • Математический анализ. 3е издание


    Скачать 4.86 Mb.
    Название3е издание
    АнкорМатематический анализ
    Дата04.02.2022
    Размер4.86 Mb.
    Формат файлаpdf
    Имя файлаpython_01.pdf
    ТипДокументы
    #351981
    страница71 из 98
    1   ...   67   68   69   70   71   72   73   74   ...   98

    Классы наследуют атрибуты своих суперклассов. Как экземпляры наследуют имена атрибутов, определяемых их классами, так же и классы наследуют все имена атрибутов, определяемые в их супер
    классах, – интерпретатор автоматически отыскивает их, когда к ним выполняется обращение, если эти атрибуты отсутствуют в подклассах.

    Экземпляры наследуют атрибуты всех доступных классов. Каж
    дый экземпляр наследует имена из своего класса, а также из всех его суперклассов. Во время поиска имен интерпретатор проверяет сначала экземпляр, потом его класс, а потом все суперклассы.

    Каждое обращение object.attribute вызывает новый независимый
    поиск. Интерпретатор выполняет отдельную процедуру поиска в де
    реве классов для каждого атрибута, который ему встречается в вы
    ражении запроса. Сюда входят ссылки на экземпляры и классы из инструкции class (например, X.attr), а также ссылки на атрибуты аргумента экземпляра self в методах класса. Каждое выражение self.attr в методе вызывает поиск attr в self и выше.

    Изменения в подклассах не затрагивают суперклассы. Замещение имен суперкласса в подклассах ниже в иерархии (в дереве) изменя
    ет подклассы и тем самым изменяет унаследованное поведение.
    Результат и главная цель такой процедуры поиска состоит в том, что классы обеспечивают разложение на отдельные операции и адаптацию

    Классы адаптируются посредством наследования
    583
    программного кода лучше, чем это могут сделать другие компоненты языка, которые мы рассматривали ранее. С одной стороны, классы по
    зволяют минимизировать избыточность программного кода (и тем са
    мым снизить стоимость обслуживания) за счет создания единой и об
    щей реализации операций, а с другой стороны, они обеспечивают воз
    можность адаптировать уже существующий программный код вместо того, чтобы изменять его или писать заново.
    Второй пример
    Этот второй пример основан на предыдущем. Для начала мы опреде
    лим новый класс, SecondClass, который наследует все имена из класса
    FirstClass и добавляет свои собственные:
    >>> class SecondClass(FirstClass): # Наследует setdata
    ... def display(self): # Изменяет display
    ... print 'Current value = "%s"' % self.data
    Класс SecondClass определяет метод display, осуществляющий вывод в другом формате. Определяя атрибут с тем же именем, что и атрибут в классе FirstClass, класс SecondClass замещает атрибут display своего суперкласса.
    Вспомните, что поиск в дереве наследования выполняется снизу вверх –
    от экземпляров к классам и далее к суперклассам и останавливается,
    как только будет найдено первое вхождение искомого имени атрибута.
    В данном случае имя display в классе SecondClass будет найдено рань
    ше, чем это же имя в классе FirstClass. В этом случае мы говорим, что класс SecondClass переопределяет метод display класса FirstClass. Ино
    гда такая замена атрибутов за счет их переопределения ниже в дереве классов называется перегрузкой.
    Главный результат здесь состоит в том, что класс SecondClass специа
    лизирует класс FirstClass, изменяя поведение метода display. С другой стороны, класс SecondClass (и все экземпляры, созданные из него) по
    прежнему наследует из класса FirstClass метод setdata. Создадим эк
    земпляр, чтобы продемонстрировать это:
    >>> z = SecondClass()
    >>> z.setdata(42) # Найдет setdata в FirstClass
    >>> z.display() # Найдет переопределенный метод в SecondClass
    Current value = "42"
    Как и прежде, мы создали объект экземпляра, вызвав класс SecondClass.
    При обращении к setdata все так же вызывается версия метода из First
    Class
    , но при обращении к атрибуту display, вызывается версия метода из SecondClass, которая выводит измененный текст сообщения. Схема пространств имен, вовлеченных в действие, изображена на рис. 23.2.
    Здесь очень важно заметить следующее: специализация, выполненная в классе SecondClass, находится полностью за пределами класса First

    584
    Глава 23. Основы программирования классов
    Class
    . Как следствие она не оказывает влияния на уже созданные или создаваемые впоследствии объекты класса FirstClass, такие как x в пре
    дыдущем примере:
    >>> x.display() # x попрежнему экземпляр FirstClass (старое сообщение)
    New value
    Вместо того чтобы изменять класс FirstClass, мы адаптировали его.
    Безусловно, это достаточно искусственный пример, но он демонстри
    рует, как наследование позволяет вносить изменения с помощью внешних компонентов (то есть с помощью подклассов). Часто классы поддерживают возможность расширения и повторного использования гораздо лучше, чем функции или модули.
    Классы – это атрибуты в модулях
    Прежде чем двинуться дальше, вспомним, что в именах классов нет ни
    чего необычного. Это всего лишь переменные, которым присваиваются объекты во время выполнения инструкций class, а ссылки на объекты можно получить с помощью обычных выражений. Например, если бы определение класса FirstClass находилось в файле модуля, а не было введено в интерактивной оболочке, мы могли бы импортировать этот модуль и использовать имя в строке заголовка инструкции class:
    from modulename import FirstClass # Скопировать имя в мою область видимости
    class SecondClass(FirstClass): # Использовать имя класса непосредственно
    def display(self): ...
    Или эквивалентный вариант:
    import modulename # Доступ ко всему модулю целиком
    class SecondClass(modulename.FirstClass): # Указать полное имя
    def display(self): ...
    Имена классов, как и все остальное, всегда находятся в модулях, и по
    тому при их употреблении необходимо следовать правилам, которые
    наследует
    Z (экземпляр)
    наследует
    SecondClass
    FirstClass
    – data
    – display
    – setdata
    – display
    Z.data
    Z.display
    Z.setdata
    Рис. 23.2. Специализация путем переопределения унаследованных имен
    посредством повторного их определения ниже в дереве классов. Здесь класс
    SecondClass переопределяет и тем самым адаптирует метод «display»
    для экземпляров этого класса

    Классы адаптируются посредством наследования
    585
    мы рассматривали в пятой части книги. Например, в одном файле мо
    дуля могут находиться определения сразу нескольких классов – по
    добно другим инструкциям в модуле, инструкции class выполняются в ходе операции импортирования и определяют имена, которые в свою очередь становятся атрибутами модуля. Вообще любой модуль может содержать самые произвольные сочетания из любого числа перемен
    ных, функций и классов, и все эти имена будут вести себя в модуле одинаково. Это демонстрирует файл food.py:
    # food.py var = 1 # food.var
    def func(): # food.func
    class spam: # food.spam
    class ham: # food.ham
    class eggs: # food.eggs
    Это правило остается справедливым, даже если модуль и класс имеют одинаковые имена. Например, пусть имеется следующий файл per+
    son.py
    :
    class person:
    Чтобы получить доступ к классу, нам необходимо обратиться к моду
    лю, как обычно:
    import person # Импортировать модуль
    x = person.person() # Класс внутри модуля
    Хотя этот способ может показаться избыточным, он совершенно необ
    ходим: имя person.person ссылается на класс person внутри модуля per
    son
    . Если использовать просто имя person, мы обратимся к модулю,
    а не к классу; кроме случая, когда используется инструкция from:
    from person import person # Получить класс из модуля
    x = person() # Использовать имя класса
    Как и любые другие переменные, мы не увидим класс в файле модуля,
    пока не импортируем его или както иначе не извлечем класс из вме
    щающего файла. Если вам это кажется сложным, то не используйте одинаковые имена для модулей и классов в них.
    Кроме того, имейте в виду: несмотря на то, что классы и модули явля
    ются пространствами имен для подключения атрибутов, они представ
    ляют собой совершенно разные структуры: модуль является отражени
    ем целого файла, а класс – это лишь инструкция внутри файла. Под
    робнее об этих различиях мы поговорим позднее в этой части книги.

    586
    Глава 23. Основы программирования классов
    Классы могут переопределять
    операторы языка Python
    Теперь давайте рассмотрим третье основное отличие классов и моду
    лей: перегрузку операторов. Говоря простым языком, перегрузка опе
    раторов позволяет объектам, созданным из классов, перехватывать и участвовать в операциях, которые применяются к встроенным типам:
    сложение, получение среза, вывод, дополнение имен и т. д. По большей части это автоматический механизм: при выполнении выражений и других встроенных операций интерпретатор передает управление реализации классов. В модулях нет ничего подобного: модули могут реализовать функции, но не операторы выражений.
    Мы можем полностью реализовать поведение класса в виде методов,
    однако перегрузка операторов позволяет объектам теснее интегриро
    ваться в объектную модель языка Python. Кроме того, перегрузка опе
    раторов помогает нашим объектам действовать так же, как действуют встроенные объекты, потому что она позволяет создавать менее проти
    воречивые и более простые в изучении интерфейсы объектов и обеспе
    чивает возможность обрабатывать объекты, созданные из классов,
    программным кодом, который предполагает взаимодействие со встро
    енными типами.

    Имена методов, начинающиеся и заканчивающиеся двумя симво!
    лами подчеркивания (__X__), имеют специальное назначение. Пе
    регрузка операторов в языке Python реализуется за счет создания методов со специальными именами для перехватывания операций.
    Язык Python определяет фиксированные и неизменяемые имена методов для каждой из операций.

    Такие методы вызываются автоматически, когда экземпляр участ!
    вует во встроенных операциях. Например, если объект экземпляра наследует метод __add__, этот метод будет вызываться всякий раз,
    когда объект будет появляться в операции сложения (+). Возвра
    щаемое значение метода становится результатом соответствующей операции.

    Классы могут переопределять большинство встроенных операто!
    ров. Существует множество специальных имен методов для реали
    зации перегрузки почти всех операторов, доступных для встроен
    ных типов. Сюда входят операторы выражений, а также такие базо
    вые операции, как вывод и создание объекта.

    В методах перегрузки операторов не существует аргументов со зна!
    чениями по умолчанию, и ни один из таких методов не является
    обязательным для реализации. Если класс не определяет и не на
    следует методы перегрузки операторов, это означает лишь то, что экземпляры класса не поддерживают эти операции. Например, ес
    ли отсутствует метод __add__, попытка выполнить операцию сложе
    ния + будет приводить к возбуждению исключения.

    Классы могут переопределять операторы языка Python
    587

    Операторы позволяют интегрировать классы в объектную модель
    языка Python. Благодаря перегрузке операторов объекты, реализо
    ванные на базе классов, действуют подобно встроенным типам и тем самым обеспечивают непротиворечивые и совместимые интерфейсы.
    Перегрузка операторов является необязательной функциональной особенностью – она используется в основном специалистами, создаю
    щими инструментальные средства для других программистов, а не разработчиками прикладных программ. И честно говоря, вам не стоит использовать ее только потому, что это «круто». Если не требуется,
    чтобы класс имитировал поведение встроенных типов, лучше ограни
    читься использованием простых методов. Например, зачем приложе
    нию, работающему с базой данных служащих, поддержка таких опе
    раторов, как * и +? Методы с обычными именами, такими как giveRaise и promote, обычно более уместны.
    Вследствие этого мы не будем в этой книге углубляться в подробности реализации методов перегрузки каждого оператора, доступного в язы
    ке Python. Однако имеется один метод перегрузки оператора, который можно встретить практически в любом классе: метод __init__, кото
    рый известен как конструктор и используется для инициализации состояния объектов. Методу __init__ следует уделить особое внима
    ние, потому что он, наряду с аргументом self, является одним из клю
    чей к пониманию ООП в языке Python.
    Третий пример
    На этот раз мы определим подкласс от SecondClass, реализующий три специальных метода, которые будут вызываться интерпретатором ав
    томатически: метод __init__ вызывается, когда создается новый объ
    ект экземпляра (аргумент self представляет новый объект ThirdClass),
    и методы __add__ и __mul__ вызываются, когда экземпляр ThirdClass участвует в операциях + или *, соответственно. Ниже приводится опре
    деление нашего класса:
    >>> class ThirdClass(SecondClass): # Наследует SecondClass
    ... def __init__(self, value): # Вызов "ThirdClass(value)"
    ... self.data = value
    ... def __add__(self, other): # Для выражения "self + other"
    ... return ThirdClass(self.data + other)
    ... def __mul__(self, other):
    ... self.data = self.data * other # Для выражения "self * other"
    >>> a = ThirdClass("abc") # Вызывается новый метод __init__
    >>> a.display() # Унаследованный метод
    Current value = "abc"
    >>> b = a + 'xyz' # Новый __add__: создается новый экземпляр
    >>> b.display()
    Current value = "abcxyz"

    588
    Глава 23. Основы программирования классов
    >>> a * 3 # Новый __mul__: заменяется сам экземпляр
    >>> a.display()
    Current value = "abcabcabc"
    Класс ThirdClass «наследует» классу SecondClass, поэтому его экземп
    ляры наследуют метод display от SecondClass. Но теперь при создании экземпляра класса ThirdClass ему передается дополнительный аргу
    мент (например, «abc») – в виде аргумента value в конструкторе
    __init__
    , значение которого присваивается атрибуту self.data. Далее,
    объекты ThirdClass могут участвовать в операциях + и * – объект экзем
    пляра слева от оператора передается в виде аргумента self, а значение справа в виде аргумента other, как показано на рис. 23.3.
    Методы со специальными именами, такими как __init__ и __add__, на
    следуются подклассами и экземплярами, как любые другие имена, ко
    торым выполняется присваивание в инструкции class. Если методы от
    сутствуют в классе, интерпретатор, как обычно, продолжит их поиск в суперклассах. Кроме того, имена методов перегрузки операторов не являются встроенными именами или зарезервированными словами –
    это обычные атрибуты, которые отыскиваются интерпретатором в слу
    чае появления объектов в различных контекстах. Как правило, интер
    претатор вызывает их автоматически, но они могут вызываться и ва
    шим программным кодом (подробнее об этом мы поговорим позднее –
    метод __init__, например, часто вызывается вручную, с целью запус
    тить конструктор суперкласса).
    Обратите внимание, что метод __add__ создает и возвращает новый объ
    ект экземпляра этого класса (вызывая ThirdClass, которому передается значение результата), а метод __mul__ изменяет текущий объект эк
    земпляра (выполняя присваивание атрибуту аргумента self). Такое поведение отличается от поведения встроенных типов, таких как чис
    ла и строки, которые всегда создают новые объекты при выполнении оператора *. Поскольку перегрузка операторов – это в действительно
    сти всего лишь механизм отображения выражений на методы, вы мо
    a * 3
    _mul_(self, other)
    Рис. 23.3. Механизм перегрузки отображает операторы выражений
    и другие встроенные операции, выполняемые над экземплярами классов,
    на специальные имена методов в классе. Эти специальные методы являются
    необязательными и могут наследоваться, как обычные методы. Здесь
    использование оператора «*» приводит к вызову метода «__mul__»

    Самый простой в мире класс на языке Python
    589
    жете интерпретировать операторы в своих объектах классов, как вам будет угодно.
    1
    C другой стороны, вы могли бы использовать перегрузку операторов,
    чтобы иметь возможность передавать объекты, определяемые пользо
    вателем, в функцию, которая выполняет операции, поддерживаемые встроенными типами, такими как списки или словари. Наличие реа
    лизации того же самого набора операторов в вашем классе обеспечит поддержку вашими объектами тех же самых интерфейсов и, как след
    ствие, совместимость с используемой функцией.
    Один из методов перегрузки присутствует практически во всех реали
    стичных классах: методконструктор __init__. Он позволяет классам не
    медленно заполнять атрибуты вновь созданных экземпляров, поэтому конструктор полезно использовать практически во всех разновидностях ваших классов. Фактически даже при том, что в языке Python атрибуты не объявляются, вы без труда сможете определить, какие атрибуты при
    надлежат экземплярам, просмотрев программный код метода __init__.
    В этой книге мы не будем углубляться в изучение этой расширенной осо
    бенности, но мы еще будем рассматривать некоторые приемы использо
    вания механизма наследования и перегрузки операторов в главе 24.
    Самый простой в мире класс на языке Python
    В этой главе мы приступили к детальному изучению синтаксиса инст
    рукции class, но я еще раз хотел бы напомнить вам, что сама модель наследования, на которой основаны классы, очень проста – суть ее за
    ключается всего лишь в организации поиска атрибутов в деревьях взаимосвязанных объектов. Фактически мы можем создавать вообще пустые классы. Следующая инструкция создает класс без присоеди
    ненных к нему атрибутов (объект пустого пространства имен):
    >>>
    1   ...   67   68   69   70   71   72   73   74   ...   98


    написать администратору сайта