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

  • Объекты-методы Обычно, метод вызывают сразу после его связывания [с функцией]:68 Учебник Python 3.1

  • Наследование Конечно же, не поддерживай «класс» наследование, не стоило бы называть его «классом». Синтаксис производного класса выглядит так:class

  • Множественное наследование

  • Учебник Python 3.1

  • Исключения — тоже классы

  • Учебник Python 3.1. Учебник Python 3. Учебник Python 1 Материал из Викиучебника. Оглавление


    Скачать 1.85 Mb.
    НазваниеУчебник Python 1 Материал из Викиучебника. Оглавление
    АнкорУчебник Python 3.1.pdf
    Дата05.04.2017
    Размер1.85 Mb.
    Формат файлаpdf
    Имя файлаУчебник Python 3.1.pdf
    ТипУчебник
    #4516
    страница10 из 13
    1   ...   5   6   7   8   9   10   11   12   13
    class
    Complex:
    def
    __init__
    (
    self
    ,
    realpart
    ,
    imagpart):
    self
    .r
    =
    realpart self
    .i
    =
    imagpart
    >>>
    x
    =
    Complex(
    3.0
    ,
    -
    4.5
    )
    >>>
    x.r
    ,
    x.i
    (
    3.0
    ,
    -
    4.5
    )
    Объекты-экземпляры
    Теперь, что же мы можем делать с объектами-экземплярами? Единственные операции, доступные объектам-экземплярам — это ссылки на атрибуты. Есть два типа корректных имён атрибутов — это атрибуты-данные и методы.
    Атрибуты-данные (data attributes) аналогичны «переменным экземпляров» в Smalltalk и
    «членам-данным» в C++. Атрибуты-данные не нужно описывать: как и переменные, они начинают существование в момент первого присваивания. Например, если x
    — экземпляр созданного выше
    MyClass
    , следующий отрывок кода выведет значение
    16
    , не вызвав ошибок:
    x.counter
    =
    1
    while
    x.counter
    <
    10
    :
    x.counter
    =
    x.counter *
    2
    print
    (x.counter)
    del
    x.counter
    Другой тип ссылок на атрибуты экземпляра — это метод (method). Метод — это функция,
    «принадлежащая» объекту. (В Python термин не уникален для экземпляров класса: другие объекты также могут иметь методы. Например, объекты-списки имеют методы append
    , insert
    , remove
    , sort и т.п. Тем не менее, ниже под термином «метод» мы будем понимать только методы объектов-экземпляров классов, пока отдельно не будет указано иное.)
    Корректные имена методов объектов-экземпляров зависят от их класса. По определению, все атрибуты класса, являющиеся объектами-функциями, описывают соответствующие методы его экземпляров. Так, в нашем примере, x.f является корректной ссылкой на метод, а x.i ей не является, поскольку не является и
    MyClass.i
    . Но при этом x.f
    — это не то же самое, что
    MyClass.f
    : это объект-метод, а не объект-функция.
    Объекты-методы
    Обычно, метод вызывают сразу после его связывания [с функцией]:
    68

    Учебник Python 3.1: Материал из Викиучебника.
    x.f()
    На примере
    MyClass такой код возвратит строку 'привет мир'
    . Однако, не обязательно вызывать метод так уж сразу: x.f
    — это объект-метод, он может быть отложен и вызван когда-либо позже. Например:
    xf
    =
    x.f
    while
    True
    :
    print
    (xf())
    будет печатать 'привет мир'
    до конца времён.
    Что конкретно происходит при вызове метода? Вы, возможно, заметили, что x.f()
    выше был вызван без аргументов, хотя в описании функции f
    аргумент был указан. Что же случилось с аргументом? Несомненно, Python порождает исключение когда функция, требующая присутствия аргумента, вызвана без единого — даже, если он на самом деле не используется...
    Теперь вы, возможно, догадались: отличительная особенность методов состоит в том, что в качестве первого аргумента функции передаётся объект. В нашем примере вызов x.f()
    полностью эквивалентен вызову
    MyClass.f(x)
    . В общем случае, вызов метода со списком из n аргументов эквивалентен вызову соответствующей функции со списком аргументов, созданным за счёт вставки объекта, вызвавшего метод, перед первым аргументом.
    Если вы всё ещё не поняли, как работают методы, взгляд на реализацию возможно прояснит происходящее. Когда атрибут экземпляра ссылается на что-либо, не являющееся атрибутом-данными, производится поиск по классу. Если имя указывает корректный атрибут класса, являющийся объектом-функцией, создаётся метод: через упаковку
    (указателя на) объекта-экземпляра и найденного объекта-функции в абстрактный объект, получается объект-метод. Когда объект-метод вызывается со списком аргументов, он снова распаковывается и новый список аргументов конструируется из объекта-экземпляра и оригинального списка аргументов, и затем уже с новым списком аргументов вызывается объект-функция.
    Различные замечания
    Атрибуты-данные переопределяют атрибуты-методы с тем же именем; для того, что обезопасить себя от случайных конфликтов имён, которые могут привести к трудно- обнаруживаемым ошибкам в больших программах, разумно использовать какое-нибудь соглашение, которое могло бы уменьшить шансы возникновения конфликтов. Возможные соглашения включают в себя: написание имён методов строчными буквами, предварение имени атрибутов-данных некоторой короткой уникальной строкой (предположим, лишь символом подчёркивания ("
    _
    ")), или использование глаголов для именования методов и существительных для именования данных.
    Методы могут ссылаться на атрибуты-данные также как и обычные пользователи
    («клиенты») объекта. Другими словами, классы не подходят для разработки чистых абстрактных типов данных. Фактически же в Python нет ничего, вынуждающего вас скрывать данные: сокрытие основано на соглашении между программистами. (С другой стороны, реализация Python, написанная на C, может полностью скрывать детали разработки и, если нужно, контролировать доступ к объекту, это можно делать в расширениях для Python, написанных на C.)
    69

    Учебник Python 3.1: Материал из Викиучебника.
    Клиенты должны использовать атрибуты-данные с осторожностью, так как иначе они могут нарушить инварианты, подразумеваемые методами класса при использовании атрибутов-данных. Заметьте, что обычно клиенты могут добавлять собственные атрибуты- данные к объектам-экземплярам, не нарушая работы методов, если не происходит конфликтов имён. Опять же, соглашение об именовании может избавить вас от головной боли и в этих случаях.
    (Прим. перев.) Автором здесь не упомянут механизм свойств (property). Свойство синтаксически является атрибутом-данным, но за кулисами с ним могут быть связаны отдельные методы для чтения, записи и удаления. Документация к функции- декоратору property хорошо проясняет суть дела:
    >>>
    print
    (
    property
    .__doc__)
    property
    (fget
    =
    None
    ,
    fset
    =
    None
    ,
    fdel
    =
    None
    ,
    doc
    =
    None
    ) -
    >
    property attribute fget - функция для чтения
    ,
    fset - для записи
    ,
    fdel - для удаления атрибута.
    Типичный пример использования для управляемого атрибута x:
    class
    C(
    object
    ):
    def
    getx(
    self
    ):
    return
    self
    ._x
    def
    setx(
    self
    ,
    value): self
    ._x
    =
    value
    def
    delx(
    self
    ):
    del
    self
    ._x x
    =
    property
    (getx
    ,
    setx
    ,
    delx
    ,
    "Я - свойство 'x'."
    )
    Декораторы упрощают определение новых свойств и изменение существующих:
    class
    C(
    object
    ):
    @
    property
    def
    x(
    self
    ):
    return
    self
    ._x
    @
    x.setter
    def
    x(
    self
    ,
    value): self
    ._x
    =
    value
    @
    x.deleter
    def
    x(
    self
    ):
    del
    self
    ._x
    (Конец прим.)
    У методов нет краткой записи для ссылок изнутри на атрибуты-данные (и другие методы!).
    Я нахожу, что это и вправду повышает читабельность методов: нет шанса спутать локальные переменные и переменные экземпляров при просмотре тела метода.
    Обычно, первый аргумент метода называется self
    . Это не более чем соглашение: имя self не имеет абсолютно никакого специального смысла для языка Python. (Однако, обратите внимание, что если вы не следуете соглашениям, ваш код может стать менее читабелен для других программистов; и также, потенциально, программа навигации по классам может опираться на такие соглашения.)
    Любой объект-функция, являющийся атрибутом класса, определяет метод для экземпляров этого класса. Не так важно, чтобы текст определения функции был заключен в определение класса: присваивание объекта-функции локальной переменной класса также работает неплохо. Например:
    # Функция, определённая вне класса
    def
    f1(
    self
    ,
    x
    ,
    y):
    return
    min
    (x
    ,
    x+y)
    class
    C:
    f
    =
    f1
    def
    g(
    self
    ):
    70

    Учебник Python 3.1: Материал из Викиучебника.
    return
    'привет мир'
    h
    =
    g
    Теперь f
    , g
    и h
    — все являются атрибутами класса
    C
    , ссылающимися на объекты-функции, и следовательно, все они являются методами экземпляров
    C
    — h
    становится полностью эквивалентен g
    . Заметьте, что такая практика обычно лишь запутывает читателя программы.
    Методы могут вызывать другие методы за счёт использования атрибутов-методов аргумента self
    :
    class
    Bag:
    def
    __init__
    (
    self
    ):
    self
    .data
    =
    []
    def
    add(
    self
    ,
    x):
    self
    .data.append(x)
    def
    addtwice(
    self
    ,
    x):
    self
    .add(x)
    self
    .add(x)
    Методы могут ссылаться на глобальные имена таким же образом, как и обычные функции.
    Глобальная область видимости, связанная с методом — это модуль, содержащий определение класса. (Сам класс никогда не используется в качестве глобальной области видимости!) В то время, как одни редко находят причины для использования глобальных данных в методах, существует множество разумных причин использовать глобальную область видимости: для примера, функции и модули, импортированные в глобальную область видимости, могут использоваться в методах так же, как в функциях и классах, в ней определённых. Обычно класс, содержащий метод, сам определён в этой глобальной области видимости, и в следующем разделе мы найдём пару хороших причин, почему методу может быть необходимо ссылаться на собственный класс!
    Наследование
    Конечно же, не поддерживай «класс» наследование, не стоило бы называть его «классом».
    Синтаксис производного класса выглядит так:
    class
    ИмяПроизводногоКласса(ИмяБазовогоКласса):
    <
    оператор-
    1
    >
    <
    оператор-N
    >
    Имя
    ИмяБазовогоКласса
    должно быть определено в области видимости, содержащей определение производного класса. Вместо имени базового класса также позволяется использовать другие выражения. Это может быть полезно, например, когда базовый класс определён в другом модуле:
    class
    ИмяПроизводногоКласса(имямодуля.ИмяБазовогоКласса):
    Использование определения производного класса проходит таким же образом, как и базового. Базовый класс полностью сохраняется по завершению конструирования объекта- класса. Такой метод используется для разрешения ссылок на атрибуты
    [55]
    : если запрошенный атрибут не был найден в самом классе, поиск продолжается в базовом классе. Правило применяется рекурсивно, если базовый класс сам является производным от некоторого другого класса.
    71

    Учебник Python 3.1: Материал из Викиучебника.
    В создании экземпляров производных классов нет ничего особенного:
    ИмяПроизводногоКласса()
    создаёт новый экземпляр класса. Ссылки на методы разрешаются следующим образом: производится поиск соответствующего атрибута класса
    (спускаясь вниз по цепочке базовых классов, если необходимо) и ссылка на метод считается корректной, если она порождает объект-функцию.
    Производные классы могут перегружать методы своих базовых классов. Поскольку у методов нет особых привилегий при вызове других методов того же объекта, метод базового класса, вызывающий другой метод, определённый в этом же классе, может вызвать перегруженный метод производного класса. (Для программистов на C++: все методы в Python фактически виртуальны.)
    При перегрузке метода в производном классе возможна не только замена действия метода базового класса с тем же именем, но и его расширение. Существует простой способ вызвать метод базового класса прямым образом: просто вызовите "
    ИмяБазовогоКласса.имяметода(self, аргументы)
    ". Такой способ будет неожиданно полезным и для клиентов. (Обратите внимание, что он работает только если базовый класс определён и импортирован прямо в глобальную область видимости.)
    В языке Python есть функции, которые работают с наследованием:

    Используйте isinstance()
    чтобы проверить тип объекта: isinstance(obj, int)
    возвратит
    True только если obj.__class__
    является int или некоторым классом, наследованным от int

    Используйте issubclass()
    чтобы проверить наследственность класса: issubclass(bool, int)
    возвратит
    True
    , поскольку класс bool является наследником (subclass)
    int
    . Однако, issubclass(float, int)
    возвратит
    False
    , поскольку класс float не является наследником int
    Множественное наследование
    Python также поддерживает форму множественного наследования (multiple inheritance). Определение класса с несколькими базовыми классами будет выглядеть так:
    class
    ИмяПроизводногоКласса(Базовый
    1
    ,
    Базовый
    2
    ,
    Базовый
    3
    ):
    <
    оператор-
    1
    >
    <
    оператор-N
    >
    В простейших случаях и для большинства задач, вы можете представлять себе поиск атрибутов, наследованных от родительского класса в виде «сперва вглубь», затем «слева- направо». Таким образом, если атрибут не найден в
    ИмяПроизводногоКласса
    , его поиск выполняется в
    Базовом1
    , затем (рекурсивно) в базовых классах
    Базового1
    и только если он там не найден, поиск перейдёт в
    Базовый2
    и так далее.
    На самом деле всё немного сложнее. Порядок разрешения методов
    [56]
    (method resolution order) меняется динамически, чтобы обеспечить возможность сотрудничающих вызовов super()
    . Этот способ известен в некоторых других языках с поддержкой множественного наследования как "вызов-следующего-метода" („call-next-method“) и имеет больше возможностей, чем вызов родительского метода в языках с единичным наследованием.
    72

    Учебник Python 3.1: Материал из Викиучебника.
    Динамическое упорядочивание (dynamic ordering) имеет важность, поскольку все вариации множественного наследования проявляют в себе эффект ромбовых отношений
    (когда как минимум один родительский класс может быть доступен различными путями из низшего в иерархии класса). Например, все классы наследуются от object
    , так что множественное наследование в любом виде предоставляет более одного пути для того, чтобы достичь object
    . Чтобы защитить базовые классы от двойных и более запросов, динамический алгоритм «выпрямляет» (linearizes) порядок поиска таким образом, что тот сохраняет указанный слева-направо порядок для каждого класса, который вызывает каждый родительский класс только единожды и является монотонным (значит, класс можно сделать наследником, не взаимодействуя с порядком предшествования его родителей). Обобщённые вместе, эти свойства позволяют разрабатывать надёжные и расширяемые классы, используя множественное наследование. С подробностями можно ознакомиться по этой ссылке: http://www.python.org/download/releases/2.3/mro/
    (
    перевод
    ).
    Приватные переменные
    В Python имеется ограниченная поддержка идентификаторов, приватных для класса.
    Любой идентификатор в форме
    __spam
    (как минимум два предшествующих символа подчёркивания, как максимум один завершающий) заменяется дословно на
    _classname__spam
    , где classname
    — текущее имя класса, лишённое предшествующих символов подчёркивания. Это искажение (mangling) производится без оглядки на синтаксическую позицию идентификатора, поэтому может использоваться для определения переменных, приватных для класса экземпляров, переменных класса, методов, переменных в глобальной области видимости (globals), и даже переменных, использующихся в экземплярах, приватных для этого класса на основе экземпляров других классов. Имя может быть обрезано, если его длина превышает 255 символов. Вне классов, или когда имя состоит из одних символов подчёркивания, искажения не происходит.
    Предназначение искажения имён состоит в том, чтобы дать классам лёгкую возможность определить «приватные» переменные экземпляров и методы, не беспокоясь о переменных экземпляров, определённых в производных классах, и о забивании кодом вне класса переменных экземпляров. Обратите внимание, что правила искажения имён разработаны, в основном, чтобы исключить неприятные случайности — решительная душа всё ещё может получить доступ или изменить переменные, предполагавшиеся приватными. В некотором особом окружении, таком как отладчик, это может оказаться полезным — и это единственная причина, по которой лазейка не закрыта. (Внимание: наследование класса с таким же именем как и у базового делает возможным использование приватных переменных базового класса.)
    Заметьте, что код, переданный в exec()
    или eval()
    , не предполагает в качестве текущего имени класса имя класса, порождающего вызов — так же, как и в случае эффекта с оператором global
    — эффекта, который также ограничен для всего побайтно- компилирующегося кода. И, такое же ограничение применимо для функций getattr()
    , setattr()
    и delattr()
    , и также для прямой ссылки на
    __dict__
    73

    Учебник Python 3.1: Материал из Викиучебника.
    Всякая всячина
    Иногда бывает полезен тип данных, похожий на record из языка Pascal или struct из языка C, например, для хранения нескольких поименованных элементов данных. Для этой цели подойдет даже пустое определение класса
    [57]
    :
    class
    Employee:
    pass
    john
    =
    Employee()
    # Создать пустую запись о рабочем
    # Заполнить поля записи
    john.name
    =
    'John Doe'
    john.dept
    =
    'computer lab'
    john.salary
    =
    1000
    Фрагменту кода на Python, требующему на входе некоторого абстрактного типа данных, можно дать экземпляр, эмулирующий методы этого типа данных. Например, если имеется функция, умеющая форматировать данные из файлового объекта, то можно определить класс с методами read()
    и readline()
    (работающие с данными, скажем, из строкового буфера) и передать ей экземпляр этого класса в качестве аргумента.
    Объекты-методы экземпляров также имеют атрибуты: m.__self__
    — исходный объект- экземпляр с методом m()
    , а m.__func__
    — объект-функция, соответствующий методу.
    Исключения — тоже классы
    Исключения, определённые пользователем, могут быть также отождествлены с классами.
    При использовании этого механизма становится возможным создавать расширяемые иерархии исключений.
    Оператор raise имеет следующие (синтаксически) правильные формы:
    1   ...   5   6   7   8   9   10   11   12   13


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