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

  • Пространства имен: окончание истории

  • Простые имена: глобальные, пока не выполняется присваивание

  • Имена атрибутов: пространства имен объектов

  • «Дзен» пространств имен в Python: классификация имен происходит при присваивании

  • Словари пространств имен

  • X = sub() >>> X.__dict__ { }>>> X.__class__ >>> sub.__bases__

  • Y = sub() >>> X.hello() >>> X.__dict__ {data1: spam}>>> X.hola() >>> X.__dict__ {data1: spam, data2: eggs}>>> sub.__dict__

  • X.data1, X.__dict__[data1] (spam, spam)>>> X.data3 = toast >>> X.__dict__

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


    Скачать 4.86 Mb.
    Название3е издание
    АнкорМатематический анализ
    Дата04.02.2022
    Размер4.86 Mb.
    Формат файлаpdf
    Имя файлаpython_01.pdf
    ТипДокументы
    #351981
    страница76 из 98
    1   ...   72   73   74   75   76   77   78   79   ...   98
    622
    Глава 24. Подробнее о программировании классов несмотря на то что реализация графического интерфейса предполагает вызывать обработчики событий как обычные функции без аргументов:
    cb1 = Callback('blue') # 'Запомнить' голубой цвет
    cb2 = Callback('green')
    B1 = Button(command=cb1) # Зарегистрировать обработчик
    B2 = Button(command=cb2) # Зарегистрировать обработчик
    Когда позднее кнопка будет нажата, объект экземпляра будет вызван как простая функция, точно как в следующих ниже вызовах. А по
    скольку он сохраняет информацию о состоянии в атрибутах экземпля
    ра, он помнит, что необходимо сделать:
    cb1() # По событию: выведет 'blue'
    cb2() # Выведет 'green'
    Фактически это один из лучших способов сохранения информации о состоянии в языке Python – он намного лучше способов, обсуждав
    шихся ранее и применявшихся к функциям (глобальные переменные,
    ссылки в область видимости объемлющей функции и изменяемые аргу
    менты со значениями по умолчанию). Благодаря ООП состояние можно сохранять явно, посредством присваивания значений атрибутам.
    Прежде чем двинуться дальше, рассмотрим еще два способа, которые используются программистами для сохранения информации о состоя
    нии в функциях обратного вызова. В первом варианте используется lambda
    функция с аргументами, имеющими значения по умолчанию:
    cb3 = (lambda color='red': 'turn ' + color) # Или: по умолчанию
    print cb3()
    Во втором используются связанные методы класса и некий объект, ко
    торый запоминает экземпляр self и ссылку на функцию, так что потом можно вызывать простую функцию без использования экземпляра:
    class Callback:
    def __init__(self, color): # Класс с информацией о состоянии
    self.color = color def changeColor(self): # Обычный именованный метод
    print 'turn', self.color cb1 = Callback('blue')
    cb2 = Callback('yellow')
    B1 = Button(command=cb1.changeColor) # Ссылка, не вызов
    B2 = Button(command=cb2.changeColor) # Запоминаются функция+self
    Когда позднее кнопка будет нажата, имитируется поведение графиче
    ского интерфейса и вызывается метод changeColor, который обработает информацию о состоянии объекта:
    object = Callback('blue')
    cb = object.changeColor # Регистрация обработчика событий
    cb() # По событию выведет 'blue'

    Перегрузка операторов
    623
    Этот прием является более простым, но менее универсальным, чем пе
    регрузка операции вызова с помощью метода __call__. Еще раз напом
    ню, что подробнее о связанных методах будет рассказываться в сле
    дующей главе.
    Кроме того, в главе 26 будет представлен еще один пример использова
    ния метода __call__, который будет использоваться для реализации так называемого декоратора функции – вызываемого объекта, добав
    ляющего уровень логики поверх внедренной функции. Поскольку ме
    тод __call__ позволяет присоединять информацию о состоянии к вызы
    ваемым объектам, этот прием является естественным для реализации функций, которые должны запоминать и вызывать другие функции.
    _
    _del_
    _ – это деструктор
    Конструктор __init__ вызывается во время создания экземпляра. Проти
    воположный ему метод __del__ вызывается автоматически, когда освобо
    ждается память, занятая объектом (то есть во время «сборки мусора»):
    >>> class Life:
    ... def __init__(self, name='unknown'):
    ... print 'Hello', name
    ... self.name = name
    ... def __del__(self):
    ... print 'Goodbye', self.name
    >>> brian = Life('Brian')
    Hello Brian
    >>> brian = 'loretta'
    Goodbye Brian
    Здесь, когда переменной brian присваивается строка, теряется послед
    няя ссылка на экземпляр класса Life, что приводит к вызову деструк
    тора. Этот метод удобно использовать для реализации некоторых за
    вершающих действий (таких как завершение соединения с сервером).
    Однако в языке Python по целому ряду причин деструкторы использу
    ются не так часто, как в других объектноориентированных языках программирования.
    С одной стороны, интерпретатор автоматически освобождает память, за
    нятую экземпляром, поэтому нет нужды выполнять очистку памяти в деструкторах.
    1
    С другой стороны, не всегда бывает возможным пред
    сказать, когда произойдет уничтожение экземпляра, поэтому часто луч
    1
    В текущей реализации Python на языке C кроме всего прочего нет необхо
    димости закрывать файлы в деструкторах, потому что они автоматически закрываются при уничтожении объектов файлов. Однако, как упомина
    лось в главе 9, лучше всетаки явно закрывать файлы, потому что «автома
    тическое закрытие при уничтожении объекта» – это особенность реализа
    ции, а не самого языка (в Jython это поведение может отличаться).

    624
    Глава 24. Подробнее о программировании классов ше выполнять завершающие действия в явно вызываемом методе (или в инструкции try/finally, которая описывается в следующей части кни
    ги) – в некоторых случаях в системных таблицах могут сохраняться ссылки на ваши объекты, что будет препятствовать вызову деструктора.
    Я привел достаточно много примеров использования перегрузки опе
    раторов. Большая часть других методов перегрузки работают похо
    жим образом, и все они – всего лишь ловушки для перехвата встроен
    ных операций. Некоторые методы перегрузки, например, имеют уни
    кальные списки аргументов или возвращаемые значения. Далее в кни
    ге вы увидите еще несколько примеров, но полный охват этой темы я оставляю за другими источниками информации.
    Пространства имен: окончание истории
    Теперь, когда мы уже исследовали объекты классов и экземпляров,
    повествование о пространствах имен в языке Python можно считать за
    вершенным. Для справки я напомню здесь все правила, используемые при разрешении имен. Первое, что вам нужно запомнить: составные и простые имена интерпретируются поразному, и некоторые области видимости служат для инициализации пространств имен объектов:

    Простые имена (например, X) располагаются в областях видимости.

    Составные имена атрибутов (например, object.X) принадлежат объ
    ектам пространств имен.

    Некоторые области видимости инициализируют пространства имен объектов (в модулях и классах).
    Простые имена: глобальные, пока не выполняется
    присваивание
    Поиск неполных простых имен выполняется в соответствии с правилом лексической видимости LEGB, выведенном для функций в главе 16:
    Присваивание
    (X = value)
    Операция присваивания делает имена локальными: создает или из
    меняет имя X в текущей локальной области видимости, если имя не объявлено глобальным.
    Ссылка
    (X)
    Пытается отыскать имя X в текущей локальной области видимости,
    затем в области видимости каждой из вмещающих функций, затем в текущей глобальной области видимости и, наконец, во встроен
    ной области видимости.
    Имена атрибутов: пространства имен объектов
    Полные (составные) имена атрибутов ссылаются на атрибуты конкрет
    ных объектов и к ним применяются правила, предназначенные для

    Пространства имен: окончание истории
    625
    модулей и классов. Для объектов классов и экземпляров эти правила дополняются включением процедуры поиска в дереве наследования:
    Присваивание
    (object.X = value)
    Создает или изменяет атрибут с именем X в пространстве имен объ
    екта object, и ничего больше. Восхождение по дереву наследования происходит только при попытке получить ссылку на атрибут, но не при выполнении операции присваивания.
    Ссылка
    (object.X)
    Для объектов, созданных на основе классов, поиск атрибута X про
    изводится сначала в объекте object, затем во всех классах, располо
    женных выше в дереве наследования. В случае объектов, которые создаются не из классов, таких как модули, атрибут X извлекается непосредственно из объекта object.
    «Дзен» пространств имен в Python: классификация
    имен происходит при присваивании
    Изза различий в процедурах поиска простых и составных имен и не
    скольких уровней поиска в обеих процедурах иногда бывает трудно сказать, где будет найдено имя. В языке Python место, где выполняет
    ся присваивание, имеет крайне важное значение – оно полностью оп
    ределяет область видимости или объект, где будет размещаться имя.
    Файл manynames.py ниже иллюстрирует, как эти принципы перево
    дятся в программный код, и обобщает идеи, касающиеся пространств имен, с которыми мы встречались на протяжении книги:
    # manynames.py
    X = 11 # Глобальное (в модуле) имя/атрибут (X, или manynames.X)
    def f():
    print X # Обращение к глобальному имени X (11)
    def g():
    X = 22 # Локальная (в функции) переменная (X, скрывает имя X в модуле)
    print X
    class C:
    X = 33 # Атрибут класса (C.X)
    def m(self):
    X = 44 # Локальная переменная в методе (X)
    self.X = 55 # Атрибут экземпляра (instance.X)
    В этом файле пять раз выполняется присваивание одному и тому же имени X. Однако, так как присваивание выполняется в пяти разных местах, все пять имен X в этой программе представляют совершенно разные переменные. Сверху вниз присваивание имени X приводит к созданию: атрибута модуля (11), локальной переменной в функции
    (22), атрибута класса (33), локальной переменной в методе (44) и атри
    бута экземпляра (55). Все пять переменных имеют одинаковые имена,

    626
    Глава 24. Подробнее о программировании классов однако они создаются в разных местах программного кода или в раз
    ных объектах, что делает их уникальными переменными.
    Не следует торопиться тщательно изучать этот пример, потому что в нем собраны идеи, которые мы исследовали на протяжении последних не
    скольких частей этой книги. Когда до вас дойдет его смысл, вы достиг
    нете своего рода нирваны пространств имен в языке Python. Конечно,
    существует и другой путь к нирване – просто запустите программу и посмотрите, что произойдет. Ниже приводится остаток этого файла,
    где создается экземпляр и выводятся значения всех имеющихся пере
    менных X:
    # manynames.py, продолжение if __name__ == '__main__':
    print X # 11: модуль (за пределами файла manynames.X)
    f() # 11: глобальная
    g() # 22: локальная
    print X # 11: переменная модуля не изменилась
    obj = C() # Создать экземпляр
    print obj.X # 33: переменная класса, унаследованная экземпляром
    obj.m() # Присоединить атрибут X к экземпляру
    print obj.X # 55: экземпляр
    print C.X # 33: класс (она же obj.X, если в экземпляре нет X)
    #print c.m.X # ОШИБКА: видима только в методе
    #print f.X # ОШИБКА: видима только в функции
    В комментариях отмечено, что будет выведено на экран после запуска этого файла, – прочитайте их, чтобы увидеть, к какой переменной X вы
    полняется обращение в том или ином случае. Обратите также внима
    ние, что мы можем добраться до атрибута класса (C.X), но мы никогда не сможем получить доступ к локальным переменным в функциях или методах вне соответствующих инструкций def. Локальные переменные видимы только программному коду внутри инструкции def и существу
    ют в памяти только во время выполнения функции или метода.
    Некоторые из имен, определяемых этим файлом, видимы и за преде
    лами файла, в других модулях, но не забывайте, что мы всегда долж
    ны сначала выполнять операцию импортирования, чтобы получить доступ к именам в другом файле – в конце концов, в этом заключается главная особенность модулей.
    # otherfile.py import manynames
    X = 66
    Print X # 66: здешняя глобальная переменная
    print manynames.X # 11: глобальная, ставшая атрибутом в результате импорта
    manynames.f() # 11: X в manynames, не здешняя глобальная!
    manynames.g() # 22: локальная в функции, в другом файле

    Пространства имен: окончание истории
    627
    print manynames.C.X # 33: атрибут класса в другом модуле
    I = manynames.C()
    print I.X # 33: все еще атрибут класса
    I.m()
    print I.X # 55: а теперь атрибут экземпляра!
    Обратите внимание, что manynames.f() выводит значение переменной X
    из модуля manynames, а не переменной из текущего модуля – область ви
    димости всегда определяется местоположением инструкции присваи
    вания в программном коде (то есть лексически) и не зависит от того,
    что импортируется и куда импортируется. Кроме того, обратите внима
    ние, что собственный атрибут X в экземпляре отсутствовал, пока не был вызван метод I.m() – атрибуты, как и любые другие переменные, появ
    ляются на свет во время операции присваивания, а не до нее. Обычно атрибуты экземпляра создаются за счет присваивания им начальных значений в конструкторе __init__, но это не единственная возможность.
    Конечно, вы не должны использовать одно и то же имя для обозначе
    ния всех переменных в своем сценарии! Но этот пример демонстриру
    ет, что если даже вы поступите так, пространства имен в языке Python предотвратят случайный конфликт имен, используемых в одном кон
    тексте, с именами, используемыми в другом контексте.
    Словари пространств имен
    В главе 19 мы узнали, что пространства имен модулей фактически реа
    лизуются как словари и доступны в виде встроенного атрибута
    __dict__
    . То же относится к объектам классов и экземпляров: обраще
    ние к составному имени атрибута фактически является операцией дос
    тупа к элементу словаря, а механизм наследования атрибута работает лишь как поиск в связанных словарях. Фактически объекты экземп
    ляра и класса – это в значительной степени просто словари со ссылка
    ми, ведущими вглубь интерпретатора. Интерпретатор Python обеспе
    чивает возможность доступа к этим словарям, а также к ссылкам меж
    ду ними для использования в особых случаях (например, при созда
    нии инструментальных средств).
    Чтобы понять внутреннее устройство атрибутов, давайте с помощью ин
    терактивной оболочки проследим, как растут словари пространств имен,
    когда в игру вступают классы. Сначала определим суперкласс и под
    класс с методами, которые сохраняют данные в своих экземплярах:
    >>> class super:
    ... def hello(self):
    ... self.data1 = 'spam'
    >>> class sub(super):
    ... def hola(self):
    ... self.data2 = 'eggs'

    628
    Глава 24. Подробнее о программировании классов
    Когда мы создаем экземпляр подкласса, он начинает свое существова
    ние с пустым словарем пространства имен, но имеет ссылку на класс,
    стоящий выше в дереве наследования. Фактически дерево наследова
    ния доступно в виде специальных атрибутов, которые вы можете прове
    рить. В экземплярах имеется атрибут __class__, который ссылается на класс, а классы имеют атрибут __bases__, который является кортежем,
    содержащим ссылки на суперклассы выше в дереве наследования:
    >>> X = sub()
    >>> X.__dict__
    { }
    >>> X.__class__

    >>> sub.__bases__
    (,)
    >>> super.__bases__
    ()
    Так как в классах выполняется присваивание атрибутам аргумента self
    , тем самым они заполняют объекты экземпляров, то есть атрибу
    ты включаются в словари пространств имен экземпляров, но не клас
    сов. В пространство имен объекта экземпляра записываются данные,
    которые могут отличаться для разных экземпляров, и аргумент self является точкой входа в это пространство имен:
    >>> Y = sub()
    >>> X.hello()
    >>> X.__dict__
    {'data1': 'spam'}
    >>> X.hola()
    >>> X.__dict__
    {'data1': 'spam', 'data2': 'eggs'}
    >>> sub.__dict__
    {'__module__': '__main__', '__doc__': None, 'hola': 0x00A47048>}
    >>> super.__dict__
    {'__module__': '__main__', 'hello': ,
    '__doc__': None}
    >>> sub.__dict__.keys(), super.__dict__.keys()
    (['__module__', '__doc__', 'hola'], ['__module__', 'hello', '__doc__'])
    >>> Y.__dict__
    { }
    Обратите внимание на имена в словарях классов, содержащие симво
    лы подчеркивания, – эти имена определяются интерпретатором авто
    матически. Большинство из них обычно не используются в програм

    Пространства имен: окончание истории
    629
    мах, но существуют такие инструменты, которые используют некото
    рые из этих имен (например, __doc__ хранит строки документирова
    ния, обсуждавшиеся в главе 14).
    Кроме того, обратите внимание, что второй экземпляр Y, созданный в начале сеанса, попрежнему имеет пустой словарь пространства имен несмотря на то, что словарь экземпляра X заполнялся инструк
    циями присваивания в методах. Напомню еще раз, что у каждого эк
    земпляра имеется свой, независимый словарь, который изначально пуст и может быть заполнен совершенно другими атрибутами, чем пространства имен других экземпляров того же самого класса.
    Так как атрибуты фактически являются ключами словаря, существу
    ет два способа получать и изменять их значения – по составным име
    нам или индексированием по ключу:
    >>> X.data1, X.__dict__['data1']
    ('spam', 'spam')
    >>> X.data3 = 'toast'
    >>> X.__dict__
    {'data1': 'spam', 'data3': 'toast', 'data2': 'eggs'}
    >>> X.__dict__['data3'] = 'ham'
    >>> X.data3
    'ham'
    Однако такая эквивалентность применяется только к атрибутам, фак
    тически присоединенным к экземпляру. Так как обращение по состав
    ному имени также вызывает запуск процедуры поиска в дереве насле
    дования, такой способ может обеспечить доступ к атрибутам, которые нельзя получить индексированием словаря. Например, унаследован
    ный атрибут X.hello недоступен через выражение X.__dict__['hello'].
    Наконец, ниже показано, что дает применение функции dir, с которой мы встречались в главах 4 и 14, к объектам экземпляров. Эта функция применяется к объектам, имеющим атрибуты: dir(object), напомина
    ет вызов object.__dict__.keys(). Однако обратите внимание, что функ
    ция dir сортирует свой список и включает в него некоторые системные атрибуты – начиная с версии Python 2.2, функция dir также автомати
    чески собирает унаследованные атрибуты:
    1
    >>>
    1   ...   72   73   74   75   76   77   78   79   ...   98


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