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

  • Еще раз о строках документирования

  • Придется держать в уме: связанные методы и функции обратного вызова

  • В заключение В этой главе мы рассмотрели подборку типичных способов комбинирования классов для получения наибольшей пользы от их повторного ис 660

  • Закрепление пройденного Контрольные вопросы

  • Расширение встроенных типов

  • Расширение типов встраиванием

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


    Скачать 4.86 Mb.
    Название3е издание
    АнкорМатематический анализ
    Дата04.02.2022
    Размер4.86 Mb.
    Формат файлаpdf
    Имя файлаpython_01.pdf
    ТипДокументы
    #351981
    страница80 из 98
    1   ...   76   77   78   79   80   81   82   83   ...   98
    Методы – это объекты:
    связанные и несвязанные методы
    Методы – это разновидность объектов, напоминающая функции. Дос
    туп к методам класса осуществляется через экземпляр класса или че
    рез сам класс и, следовательно, в языке Python имеется две разновид
    ности методов:
    Несвязанные методы класса: без аргумента self
    Попытка обращения к функциональному атрибуту класса через имя класса возвращает объект несвязанного метода. Чтобы вызвать этот метод, необходимо явно передать ему объект экземпляра в ви
    де первого аргумента.
    Связанные методы экземпляра: пара self + функция
    Попытка обращения к функциональному атрибуту класса через имя экземпляра возвращает объект связанного метода. Интерпре

    656
    Глава 25. Шаблоны проектирования с классами татор автоматически упаковывает экземпляр с функцией в связан
    ный объект метода, поэтому вам не требуется передавать экземпляр в вызов метода.
    Обе разновидности методов – это полноценные объекты. Они могут пе
    редаваться между программными компонентами, сохраняться в спи
    сках и т. д. При запуске оба требуют наличия экземпляра в первом ар
    гументе (то есть значения для аргумента self). Именно по этой причине в предыдущей главе было необходимо явно передавать экземпляр при вызове методов суперкласса из методов подкласса – с технической точ
    ки зрения такие вызовы порождают объекты несвязанных методов.
    Вызывая объект связанного метода, интерпретатор автоматически под
    ставляет экземпляр, который использовался при создании объекта свя
    занного метода. Это означает, что объекты связанных методов обычно взаимозаменяемы с объектами простых функций и создание их особенно полезно в случае интерфейсов, изначально ориентированных на исполь
    зование функций (реалистичный пример приводится во врезке «При
    дется держать в уме: связанные методы и функции обратного вызова»).
    Чтобы проиллюстрировать вышесказанное, предположим, что имеет
    ся следующее определение класса:
    class Spam:
    def doit(self, message):
    print message
    В обычной ситуации мы создаем экземпляр и сразу же вызываем его метод для вывода содержимого аргумента:
    object1 = Spam()
    object1.doit('hello world')
    Однако в действительности попутно создается объект связанного мето
    да – как раз перед круглыми скобками в вызове метода. Т.е. мы можем получить связанный метод и без его вызова. Составное имя object.name –
    это выражение, которое возвращает объект. В следующем примере это выражение возвращает объект связанного метода, в котором упакова
    ны вместе экземпляр (object1) и метод (Spam.doit). Мы можем присво
    ить этот связанный метод другому имени и затем использовать это имя для вызова, как простую функцию:
    object1 = Spam()
    x = object1.doit # Объект связанного метода: экземпляр+функция
    x('hello world') # То же, что и object1.doit('...')
    С другой стороны, если для получения метода doit использовать имя класса, мы получим объект несвязанного метода, который просто ссы
    лается на объект функции. Чтобы вызвать метод этого типа, необходи
    мо явно передавать экземпляр класса в первом аргументе:
    object1 = Spam()
    t = Spam.doit # Объект несвязанного метода
    t(object1, 'howdy') # Передать экземпляр

    Еще раз о строках документирования
    657
    Те же самые правила действуют внутри методов класса, когда использу
    ются атрибуты аргумента self, которые ссылаются на функции в клас
    се. Выражение self.method возвращает объект связанного метода, пото
    му что self – это объект экземпляра:
    class Eggs:
    def m1(self, n):
    print n def m2(self):
    x = self.m1 # Еще один объект связанного метода
    x(42) # Выглядит как обычная функция
    Eggs().m2() # Выведет 42
    Чаще всего вы будете вызывать методы немедленно, сразу же после указания составного имени, поэтому вы не всегда будете замечать, что попутно создается объект метода. Но как только вы начнете писать программный код, который вызывает объекты единообразным спосо
    бом, то сразу обратите внимание на несвязанные методы, потому что обычно они требуют явной передачи экземпляра в первом аргументе.
    1
    Теперь, когда вы понимаете суть объектной модели методов, озна
    комьтесь с примерами применения связанных методов во врезке
    «Придется держать в уме: связанные методы и функции обратного вы
    зова» и еще раз прочитайте раздел предыдущей главы «__call__ обра
    батывает вызовы», где обсуждаются функции обратного вызова.
    Еще раз о строках документирования
    Строки документирования, которые мы подробно рассматривали в гла
    ве 14, – это литералы строк, которые присутствуют на верхнем уровне различных структур и автоматически сохраняются интерпретатором
    Python в атрибутах __doc__ соответствующих им объектов. Строки до
    кументирования могут присутствовать в модулях, в инструкциях def,
    а также в определениях классов и методов. Теперь, когда мы ближе познакомились с классами и методами, можно изучить короткий, но емкий пример docstr.py – здесь демонстрируются места в программном коде, где могут появляться строки документирования. Все они могут представлять собой блоки в тройных кавычках:
    "I am: docstr.__doc__"
    class spam:
    "I am: spam.__doc__ or docstr.spam.__doc__"
    def method(self, arg):
    "I am: spam.method.__doc__ or self.method.__doc__"
    1
    Смотрите обсуждение статических методов класса в главе 26, которые представляют собой исключение из этого правила. Подобно связанным ме
    тодам, они также могут выглядеть как обычные функции, потому что они не ожидают получить экземпляр в первом аргументе.

    658
    Глава 25. Шаблоны проектирования с классами pass def func(args):
    "I am: docstr.func.__doc__"
    pass
    Основное преимущество строк документирования состоит в том, что их содержимое доступно во время выполнения. То есть, если текст был оформлен в виде строки документирования, можно будет обратиться к атрибуту __doc__ объекта, чтобы получить его описание:
    Придется держать в уме: связанные методы
    и функции обратного вызова
    В объектах связанных методов вместе с функцией автоматически сохраняется экземпляр класса, поэтому они могут использовать
    ся везде, где используются обычные функции. Одно из обычных мест, где можно увидеть эту идею в действии, – это программный код, регистрирующий методы как обработчики событий в интер
    фейсе Tkinter GUI. Ниже приводится простейший случай:
    def handler():
    ... сохраняет информацию о состоянии в глобальных переменных...
    widget = Button(text='spam', command=handler)
    Чтобы зарегистрировать обработчик события щелчка на кнопке,
    мы обычно передаем в аргументе с именем command вызываемый объект, который не имеет входных аргументов. Здесь часто ис
    пользуются имена простых функций (и lambdaвыражения), но можно также передавать и методы классов – при условии, что они будут связанными методами:
    class MyWidget:
    def handler(self):
    ...сохраняет информацию о состоянии в self.attr...
    def makewidgets(self):
    b = Button(text='spam', command=self.handler)
    Здесь обработчик события – это объект связанного метода, где со
    храняются self и MyWidget.handler. Так как аргумент self ссылает
    ся на оригинальный экземпляр, то когда метод handler будет вы
    зван для обработки события, он получит доступ к атрибутам эк
    земпляра, где может сохраняться информация о состоянии меж
    ду событиями. При использовании обычных функций для этих целей, как правило, используются глобальные переменные. Дру
    гой способ обеспечения совместимости классов с прикладным ин
    терфейсом, основанным на применении функций, приводится в главе 24, где обсуждается метод перегрузки операторов __call__.

    Классы и модули
    659
    >>> import docstr
    >>> docstr.__doc__
    'I am: docstr.__doc__'
    >>> docstr.spam.__doc__
    'I am: spam.__doc__ or docstr.spam.__doc__'
    >>> docstr.spam.method.__doc__
    'I am: spam.method.__doc__ or self.method.__doc__'
    >>> docstr.func.__doc__
    'I am: docstr.func.__doc__'
    В главе 14 также обсуждается PyDoc – инструмент, который позволяет формировать отчеты из всех этих строк.
    Строки документирования доступны во время выполнения, но синтак
    сически они менее гибки, чем комментарии # (которые могут находить
    ся в любом месте программы). Обе формы несут пользу, и любая доку
    ментация к программе – это хорошо (при условии, что она точная).
    Классы и модули
    Мы завершаем эту главу кратким сравнением предметов обсуждения двух последних частей книги: модулей и классов. Так как оба пред
    ставляют собой пространства имен, различия между ними бывает трудно заметить сразу. В двух словах:
    Модули

    Это пакеты данных и исполняемого кода.

    Создаются как файлы с программным кодом на языке Python или как расширения на языке C.

    Задействуются операцией импортирования.
    Классы

    Реализуют новые объекты.

    Создаются с помощью инструкции class.

    Задействуются операцией вызова.

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

    660
    Глава 25. Шаблоны проектирования с классами пользования и возможности разбивать крупные задачи на более мел
    кие части, что обычно относится к проблемам проектирования, кото
    рые часто рассматриваются вне зависмости от конкретного языка про
    граммирования (хотя язык Python способен облегчить их решение).
    Мы изучили приемы делегирования (обертывание объектов в классы
    обертки), композиции (управление встраиваемыми объектами), насле
    дования (приобретение поведения от других классов) и некоторые дру
    гие не совсем обычные концепции, такие как множественное наследо
    вание, связанные методы и фабрики.
    Следующая глава завершает изучение классов и ООП рассмотрением бо
    лее сложных тем, связанных с классами. Часть этого материала может быть более интересна для тех, кто пишет инструментальные средства,
    а не прикладные программы, но эти сведения все же заслуживают того,
    чтобы с ними ознакомились большинство тех, кто занимается ООП на языке Python. Однако сначала ответьте на контрольные вопросы.
    Закрепление пройденного
    Контрольные вопросы
    1. Что такое множественное наследование?
    2. Что такое делегирование?
    3. Что такое композиция?
    4. Что такое связанные методы?
    Ответы
    1. Множественное наследование имеет место, когда класс наследует более одного суперкласса, – это удобно для объединения пакетов программного кода, оформленных в виде классов.
    2. Делегирование подразумевает обертывание объекта классомоберт
    кой, который расширяет функциональные возможности обернуто
    го объекта и передает ему выполнение части операций. Класс
    обертка сохраняет интерфейс обернутого объекта.
    3. Композиция – это прием, который подразумевает наличие контрол
    лера, куда встраиваются и которым управляются несколько объек
    тов. Класс контроллера предоставляет все интерфейсы как свои собственные – это один из способов создания крупных структур с помощью классов.
    4. Связанные методы объединяют экземпляр класса и функцию мето
    да – их можно вызывать, не передавая объект экземпляра в первом аргументе, потому что внутри таких методов попрежнему доступен оригинальный экземпляр.

    26
    Дополнительные возможности классов
    Эта глава завершает шестую часть книги и наше изучение ООП на язы
    ке Python представлением нескольких более сложных тем, связанных с использованием классов: мы рассмотрим возможность создания под
    классов встроенных типов, псевдочастные (pseudoprivate) атрибуты,
    классы нового стиля, статические функции, декораторы функций и многое другое.
    Как мы уже знаем, модель ООП в языке Python чрезвычайно проста,
    а некоторые из приемов, представленных в этой главе, настолько слож
    ны и совершенно необязательны к использованию, что вы, возможно,
    не слишком часто будете встречать их в своей карьере прикладного программиста на языке Python. Тем не менее в интересах законченно
    сти обсуждения, мы завершим наше обсуждение классов кратким обзо
    ром этих возможностей.
    Как обычно, т.к. это последняя глава в этой части, она завершается сводкой типичных проблем, с которыми сталкиваются программисты при использовании классов, и набором упражнений к этой части. Я со
    ветую вам обязательно проработать эти упражнения, чтобы прочнее ухватить идеи, которые мы изучали в этой части. Я также предлагаю вам самостоятельно познакомиться с крупными объектноориентиро
    ванными проектами на языке Python в дополнение к этой книге. Как и все в программировании, преимущества ООП становятся более оче
    видными с обретением опыта.
    Расширение встроенных типов
    Помимо реализации объектов новых типов классы иногда использу
    ются для расширения функциональных возможностей встроенных ти
    пов языка Python с целью обеспечения поддержки более экзотических структур данных. Например, чтобы добавить в списки дополнитель

    662
    Глава 26. Дополнительные возможности классов ные методы вставки и удаления, можно создать класс, который оберты
    вает (встраивает) объект списка и экспортирует методы вставки и уда
    ления, которые особым образом обрабатывают список, подобно тому,
    как реализуется прием делегирования, рассмотренный в главе 25. На
    чиная с версии Python 2.2 для специализации встроенных типов мож
    но также использовать наследование. Следующие два раздела демон
    стрируют оба приема в действии.
    Расширение типов встраиванием
    Помните те функции для работы со множествами, которые мы написа
    ли в четвертой части книги? Ниже показано, как они выглядят, реани
    мированные в виде класса на языке Python. Следующий пример (файл
    setwrapper.py
    ) реализует новый тип объектов за счет перемещения не
    скольких функций в методы и добавления перегрузки нескольких ос
    новных операторов. По большей части этот класс просто обертывает список, добавляя дополнительные операции. Поскольку это класс, он также поддерживает возможность создания множества экземпляров и адаптацию своего поведения наследованием в подклассах:
    class Set:
    def __init__(self, value = []): # Конструктор
    self.data = [] # Управляет списком
    self.concat(value)
    def intersect(self, other): # other – любая последовательность
    res = [] # self – подразумеваемый объект
    for x in self.data:
    if x in other: # Выбрать общие элементы
    res.append(x)
    return Set(res) # Вернуть новый экземпляр Set
    def union(self, other): # other – любая последовательность
    res = self.data[:] # Копировать список
    for x in other: # Добавить элементы из other
    if not x in res:
    res.append(x)
    return Set(res)
    def concat(self, value): # Аргумент value: список, Set...
    for x in value: # Удалить дубликаты
    if not x in self.data:
    self.data.append(x)
    def __len__(self): return len(self.data) # len(self)
    def __getitem__(self, key): return self.data[key] # self[i]
    def __and__(self, other): return self.intersect(other) # self & other
    def __or__(self, other): return self.union(other) # self | other
    def __repr__(self): return 'Set:' + `self.data` # Вывод
    Перегрузка операции доступа к элементам по их индексам позволяет экземплярам нашего класса Set выглядеть как настоящие списки.
    В упражнениях в конце этой главы вам будет предложено организо

    Расширение встроенных типов
    663
    вать взаимодействие с этим классом и расширить его, поэтому подроб
    нее об этом фрагменте мы поговорим в приложении B.
    Расширение типов наследованием
    Начиная с версии Python 2.2 все встроенные типы можно наследовать.
    Функции преобразования типов, такие как list, str, dict и tuple, пре
    вратились в имена встроенных типов. Теперь вызов функции преобра
    зования типа (например, list('spam')) в действительности является вызовом конструктора типа.
    Это изменение позволяет адаптировать или расширять поведение встро
    енных типов с помощью инструкций class: достаточно просто создать подклассы с новыми именами типов, где реализовать необходимые из
    менения. Экземпляры вашего нового подкласса могут использоваться везде, где допускается использовать оригинальный встроенный тип. На
    пример, предположим, вас не устраивает тот факт, что стандартные спи
    ски начинают отсчет элементов с 0, а не с 1. Это не проблема – вы всегда можете создать свой подкласс, который изменяет эту характерную осо
    бенность списков. В файле typesubclass.py показано, как это делается:
    # Подкласс встроенного типа/класса list.
    # Отображает диапазон 1..N на 0..N1; вызывает встроенную версию.
    class MyList(list):
    def __getitem__(self, offset):
    print '(indexing %s at %s)' % (self, offset)
    return list.__getitem__(self, offset  1)
    if __name__ == '__main__':
    print list('abc')
    x = MyList('abc') # __init__ наследуется из списка
    print x # __repr__ наследуется из списка
    print x[1] # MyList.__getitem__
    print x[3] # Изменяет поведение метода суперкласса
    x.append('spam'); print x # Атрибуты, унаследованные
    # от суперкласса списка
    x.reverse(); print x
    В этом файле подкласс MyList расширяет метод __getitem__ встроенных списков простым отображением диапазона значений от 1 до N на необ
    ходимый спискам диапазон от 0 до N1. Уменьшение индекса на еди
    ницу и вызов версии метода из суперкласса – вот все, что в действи
    тельности делается, но этого вполне достаточно для достижения по
    ставленной цели:
    %
    1   ...   76   77   78   79   80   81   82   83   ...   98


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