- Объектно-ориентированное программирование на Python
-
Объектно-ориентированное программирование на Python — программирование на Python с использованием парадигмы ООП: с самого начала Python проектировался как объектно-ориентированный язык программирования[1].
Содержание
Введение
Принципы ООП
Согласно Алану Кэю — автору языка программирования Smalltalk — объектно-ориентированным может называться язык, построенный с учетом следующих принципов[2]:
- Все данные представляются объектами
- Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
- Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
- Каждый объект имеет тип
- Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Объекты, типы и классы
Определение класса
Для ясности последующего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения класса используется оператор
class:class имя_класса(надкласс1, надкласс2, ...): # определения атрибутов и методов классаУ класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A: passВ терминологии Python члены класса называются атрибутами, функции класса — методами, а поля класса — свойствами (или просто атрибутами).
Определения методов аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению
self:class A: def m1(self, x): # блок кода методаОпределения атрибутов — обычные операторы присваивания, которые связывают некоторые значения с именами атрибутов.
class A: attr1 = 2 * 2В языке Python класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A: pass def myMethod(self, x): return x * x A.m1 = myMethod A.attr1 = 2 * 2Создание экземпляра
Для создания объекта — экземпляра класса (то есть, инстанциирования класса), достаточно вызвать класс по имени и задать параметры конструктора:
class Point: def __init__(self, x, y, z): self.coord = (x, y, z) def __repr__(self): return "Point(%s, %s, %s)" % self.coord>>> p = Point(0.0, 1.0, 0.0) >>> p Point(0.0, 1.0, 0.0)
Переопределив классовый метод
__new__, можно контролировать процесс создания экземпляра. Этот метод вызывается до метода__init__и должен вернуть новый экземпляр, либоNone(в последнем случае будет вызван__new__родительского класса). Метод__new__используется для управления созданием неизменчивых (immutable) объектов, управления созданием объектов в случаях, когда__init__не вызывается, например, при десериализации (unpickle). Следующий код демонстрирует один из вариантов реализации шаблона Одиночка:>>> class Singleton(object): obj = None # Атрибут для хранения единственного экземпляра def __new__(cls,*dt,**mp): # класса Singleton. if cls.obj is None: # Если он еще не создан, то cls.obj = object.__new__(cls,*dt,**mp) # вызовем __new__ родительского класса return cls.obj # вернем синглтон ... >>> obj = Singleton() >>> obj.attr = 12 >>> new_obj = Singleton() >>> new_obj.attr 12 >>> new_obj is obj # new_obj и obj - это один и тот же объект True
Конструктор и деструктор
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (деструктор). В языке Python реализовано автоматическое управление памятью, поэтому деструктор требуется достаточно редко, для ресурсов, требующих явного освобождения.
Следующий класс имеет конструктор и деструктор:
class Line: def __init__(self, p1, p2): self.line = (p1, p2) def __del__(self): print "Удаляется линия %s - %s" % self.line
>>> l = Line((0.0, 1.0), (0.0, 2.0)) >>> del l Удаляется линия (0.0, 1.0) - (0.0, 2.0) >>>
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно «истощенной», поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
Время жизни объекта
Без применения каких-либо особых средств время жизни объекта, определённого в программе на Python, не выходит за рамки времени выполнения процесса этой программы.
Для преодоления этого ограничения существуют различные возможности: от хранения объектов в простейшей базе данных (shelve), применения ORM (например, SQLAlchemy) до использования специализированных баз данных с развитыми возможностями (например, ZODB). Все эти средства позволяют делать объекты устойчивыми (англ. persistent). Как правило, при записи объекта производится его сериализация, а при чтении — десериализация.
>>> import shelve >>> s = shelve.open("somefile.db") >>> s['myobject'] = [1, 2, 3, 4, 'свечка'] >>> s.close() >>> import shelve >>> s = shelve.open("somefile.db") >>> print s['myobject'] [1, 2, 3, 4, '\xd1\x81\xd0\xb2\xd0\xb5\xd1\x87\xd0\xba\xd0\xb0']
Инкапсуляция и доступ к свойствам
Инкапсуляция является одним из ключевых понятий ООП. Все значения в Python являются объектами, инкапсулирующими код (методы) и данные и предоставляющими пользователям общедоступный интерфейс. Методы и данные объекта доступны через его атрибуты.
Сокрытие информации о внутреннем устройстве объекта выполняется в Python на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие — к его внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких языках как C++ или Java: атрибут остается доступным, но под именем вида
_ИмяКласса__ИмяАтрибута, а при каждом обращенииPythonбудет модифицировать имя в зависимости от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний классы могут иметь атрибут с именем, например, «__f», но не будут мешать друг другу.>>> class parent(object): def __init__(self): self.__f = 2 def get(self): return self.__f .... >>> class child(parent): def __init__(self): self.__f = 1 parent.__init__(self) def cget(self): return self.__f .... >>> c = child() >>> c.get() 2 >>> c.cget() 1 >>> c.__dict__ {'_child__f': 1, '_parent__f': 2} # на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени атрибута. Они используются для специальных свойств и функций класса (например, для перегрузки операции). Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object): def __init__(self, x): # атрибут получает значение в конструкторе self.x = x a = A(5) print a.x a.x = 5
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута:
class A(object): def __init__(self, x): self._x = x 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") # определяем x как свойство a = A(5) print a.x # Синтаксис доступа к атрибуту при этом прежний a.x = 5
Разумеется, первый способ хорош только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Существуют два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке методов
__getattr__(),__setattr__(),__delattr__(), а второй — метода__getattribute__(). Второй метод помогает управлять чтением уже существующих атрибутов. Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы RPC для Python, имитируя методы и свойства, реально существующие на удаленном сервере.Полиморфизм
В компилируемых языках программирования полиморфизм достигается за счёт создания виртуальных методов, которые в отличие от невиртуальных можно перегрузить в потомке. В Python все методы являются виртуальными, что является естественным следствием разрешения доступа на этапе исполнения. (Следует отметить, что создание невиртуальных методов в компилируемых языках связано с меньшими накладными расходами на их поддержку и вызов).
>>> class Parent(object): def isParOrPChild(self) : return True def who(self) : return 'parent' >>> class Child(Parent): def who(self): return 'child' >>> x = Parent() >>> x.who(), x.isParOrPChild() ('parent', True) >>> x = Child() >>> x.who(), x.isParOrPChild() ('child', True)
Явно указав имя класса, можно обратиться к методу родителя (как впрочем и любого другого объекта).
>>> class Child(Parent): def __init__(self): Parent.__init__(self)
В общем случае для получения класса-предка применяется функция
super.class Child(Parent): def __init__(self): super(Child, self).__init__(self)
Используя специально предусмотренное исключение
NotImplementedError, можно имитировать чисто виртуальные методы:>>> class abstobj(object): def abstmeth(self): raise NotImplementedError('Method abstobj.abstmeth is pure virtual') >>> abstobj().abstmeth() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in method NotImplementedError: Method abstobj.abstmeth is pure virtual
Или, с использованием декоратора, так:
>>> def abstract(func): def closure(*dt, **mp): raise NotImplementedError("Method %s is pure virtual" % func.__name__) return closure >>> class abstobj(object): @abstract def abstmeth(self): pass
Изменяя атрибут
__class__, можно перемещать объект вверх или вниз по иерархии наследования (впрочем, как и к любому другому типу)>>> c = child() >>> c.val = 10 >>> c.who() 'child' >>> c.__class__ = Parent >>> c.who() 'parent' >>> c.val 10
Однако, в этом случае никакие преобразования типов не делаются, поэтому забота о согласованности данных всецело лежит на программисте. Кроме того, присваивание атрибуту
__class__не должно применяться по поводу и без. Прежде чем решиться на его использование, необходимо рассмотреть менее радикальные варианты реализации изменения объекта, то есть по сути шаблона проектирования State.Более того, полиморфизм в Python вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism)[3]. Например, чтобы экземпляру класса «прикинуться» файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно
.read(),.readlines(),.close()и т. п.).Имитация встроенных типов
Встроенные типы и их методы имеют синтаксическую поддержку в языке Python или другие особые «привилегии». Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определённый пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример — имитировать функцию:
>>> class Add: ... def __call__(self, x, y): # определение метода, ... return x + y # который отвечает за операцию вызова функции ... >>> add = Add() >>> add(3, 4) # это эквивалентно add.__call__(3, 4) 7
Аналогично поддаются имитации все операции встроенных типов. Ещё один пример связан с вычислением длины объекта с помощью функции
len(). Оказывается, эта встроенная функция вызывает специальный метод:>>> class wrongList(list): # определяем собственный класс для списка ... def __len__(self): # который всегда считает, что имеет нулевую длину ... return 0 ... >>> w = wrongList([1,2,3]) >>> len(w) # это эквивалентно w.__len__() 0
Методы
__getitem__,__setitem__,__delitem__,__contains__позволяют создать интерфейс для словаря или списка(dict). Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию*:class Multiplyable: def __init__(self, value): self.value = value def __mul__(self, y): return self.value * y def __rmul__(self, x): return x * self.value def __imul__(self, y): return Multiplyable(self.value * y) def __str__(self): return "Multiplyable(%s)" % self.value >>> m = Multiplyable(1) >>> print m Multiplyable(1) >>> m *= 3 >>> print m Multiplyable(3)
Последний из методов —
.__str__()— отвечает за представление экземпляра класса при печати операторомprintи в других подобных случаях.Аналогичные методы имеются и у соответствующих встроенных типов
>>> int.__add__ <slot wrapper '__add__' of 'int' objects> >>> [].__getitem__ <built-in method __getitem__ of list object at 0x00DA3D28> >>> class a(object):pass >>> a.__call__ <method-wrapper '__call__' of type object at 0x00DDC318>
Не все из них существуют на самом деле: большая часть имитируется интерпретатором Python для удобства программиста. Такое поведение позволяет экономить время при наиболее важных операциях (например, сложение целых не приводит к поиску и вызову метода
__add__у классаint) и память, но приводит к невозможности изменения методов у встроенных классов.Отношения между классами
Наследование и множественное наследование
При описании предметной области классы могут образовывать иерархию, в корне которой стоит базовый класс, а нижележащие классы (подклассы) наследуют свои атрибуты, уточняя и расширяя поведение вышележащего класса (надкласса). Обычно принципом построения классификации является отношение «IS-A» («есть» — между экземпляром и классом) и «AKO» («a kind of» — «разновидность» — между классом и суперклассом)[4].
Python поддерживает как одиночное наследование, так и множественное, позволяющее классу быть производным от любого количества базовых классов.
>>> class Par1(object): # наследуем один базовый класс - object def name1(self): return 'Par1' >>> class Par2(object): def name2(self): return 'Par2' >>> class Child(Par1, Par2): # создадим класс, наследующий Par1, Par2 (и, опосредованно, object) pass >>> x = Child() >>> x.name1(), x.name2() # экземпляру Child доступны методы из Par1 и Par2 'Par1','Par2'
В Python (из-за «утиной типизации») отсутствие наследования ещё не означает, что объект не может предоставлять тот же самый интерфейс.
Множественное наследование в Python применяется в основном для добавления примесей (mixins) — специальных классов, вносящих некоторую черту поведения или набор свойств[5].
Порядок разрешения доступа к методам и полям
За достаточно простым в использовании механизмом доступа к атрибутам в Python кроется довольно сложный алгоритм. Далее будет приведена последовательность действий, производимых интерпретатором при разрешении запроса
object.field(поиск прекращается после первого успешно завершённого шага, иначе происходит переход к следующему шагу).- Если у
objectесть метод__getattribute__, то будет вызван он с параметром'field'(либо__setattr__или__delattr__в зависимости от действия над атрибутом) - Если у
objectесть поле__dict__, то ищетсяobject.__dict__['field'] - Если у
object.__class__есть поле__slots__, то'field'ищется вobject.__class__.__slots__ - Проверяется
object.__class__.__dict__['fields'] - Производится рекурсивный поиск по
__dict__всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для «классических» и «новых» классов. - Если у
objectесть метод__getattr__, то вызывается он с параметром'field' - Возбуждается исключение
AttributeError.
Если поиск окончен успешно, то проверяется, является ли атрибут классом «нового стиля». Если является, то проверяется наличие у него метода
__get__(либо__set__или__delete__, в зависимости от действия над атрибутом), если метод найден, то происходит следующий вызовobject.field.__get__(object)и возвращается его результат (такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) и используются, например, для создания свойств[6]).Эта последовательность распространяется только на пользовательские атрибуты. Системные атрибуты, такие как
__dict__,__len__,__add__и другие, имеющие специальные поля в С-структуре описания класса находятся сразу.«Новые» и «классические» классы
В версиях до 2.2 некоторые объектно-ориентированные возможности Python были заметно ограничены. Например, было невозможно наследовать встроенные классы и классы из модулей расширения. Свойства (property) не выделялись явно. Начиная с версии 2.2, объектная система Python была существенно переработана и дополнена. Однако для совместимости со старыми версиями Python было решено сделать две объектные модели: «классические» типы (полностью совместимые со старым кодом) и «новые»[7]. В версии Python3000 поддержка «старых» классов будет удалена.
Для построения «нового» класса достаточно унаследовать его от другого «нового». Если нужно создать «чистый» класс, то можно унаследоваться отobject— родительского типа для всех «новых» классов.class OldStyleClass: pass # класс "старого" типа class NewStyleClass(object): pass # и "нового"
Все стандартные классы — классы «нового» типа.[8]
Агрегация. Контейнеры. Итераторы
Агрегация, когда один объект входит в состав другого, или отношение «HAS-A» («имеет»), реализуется в Python с помощью ссылок. Python имеет несколько встроенных типов контейнеров: список, словарь, множество. Можно определить собственные классы контейнеров со своей логикой доступа к хранимым объектам. (Следует заметить, что в Python агрегацию можно считать разновидностью ассоциации, так реально объекты не вложены друг в друга в памяти и, более того, время жизни элемента может не зависеть от времени жизни контейнера.)
Следующий класс из модуля utils.py среды web.py является примером контейнера-словаря, дополненного возможностью доступа к значениям при помощи синтаксиса доступа к атрибутам:
class Storage(dict): def __getattr__(self, key): try: return self[key] except KeyError, k: raise AttributeError, k def __setattr__(self, key, value): self[key] = value def __delattr__(self, key): try: del self[key] except KeyError, k: raise AttributeError, k def __repr__(self): return '<Storage ' + dict.__repr__(self) + '>'
Вот как он работает:
>>> v = Storage(a=5) >>> v.a 5 >>> v['a'] 5 >>> v.a = 12 >>> v['a'] 12 >>> del v.a
Для доступа к контейнерам очень удобно использовать итераторы:
>>> cont = dict(a=1, b=2, c=3) >>> for k in cont: ... print k, cont[k] ... a 1 c 3 b 2
Ассоциация и слабые ссылки
Отношение использования («USE-A») экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают циклические ссылки. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Python с помощью механизма подсчета ссылок. Удалением таких объектов занимается сборщик мусора.
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью слабых ссылок. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль
weakref.Метаклассы
Обычных возможностей объектно-ориентированного программирования хватает далеко не всегда. В некоторых случаях требуется изменить сам характер системы классов: расширить язык новыми типами классов, изменить стиль взаимодействия между классами и окружением, добавить некоторые дополнительные аспекты, затрагивающие все используемые в приложении классы, и т. п.
При объявлении метакласса за основу можно взять класс
type. Пример:# описание метакласса class myobject(type): # небольшое вмешательство в момент выделения памяти для класса def __new__(cls, name, bases, dict): print "NEW", cls.__name__, name, bases, dict return type.__new__(cls, name, bases, dict) # небольшое вмешательство в момент инициализации класса def __init__(cls, name, bases, dict): print "INIT", cls.__name__, name, bases, dict return super(myobject, cls).__init__(name, bases, dict) # порождение класса на основе метакласса (заменяет оператор class) MyObject = myobject("MyObject", (), {}) # обычное наследование другого класса из только что порожденного class MySubObject(MyObject): def __init__(self, param): print param # получение экземпляра класса myobj = MySubObject("parameter")
Разумеется, вместо оператора
printкод метакласса может выполнять более полезные функции: регистрировать класс, передавать действия с классами на удаленную систему, использовать классы для других целей (например, как декларации или ограничения) и т. п.Методы
Метод
Синтаксис описания метода ничем не отличается от описания функции, разве что его положением внутри класса и характерным первым формальным параметром
self, с помощью которого внутри метода можно ссылаться на сам экземпляр класса (название self является соглашением, которого придерживаются программисты на Python):class MyClass(object): def mymethod(self, x): return x == self._x
Статический метод
Статические методы в Python являются синтаксическими аналогами статических функций в основных языках программирования. Они не получают ни экземпляр (
self), ни класс (cls) первым параметром. Для создания статического метода (только «новые» классы могут иметь статические методы) используется декораторstaticmethod>>> class D(object): @staticmethod def test(x): return x == 0 ... >>> D.test(1) # доступ к статическому методу можно получать и через класс False >>> f = D() >>> f.test(0) # и через экземпляр класса True
Статические методы реализованы с помощью свойств (property).
Метод класса
Классовые методы в Python занимают промежуточное положение между статическими и обычными. В то время как обычные методы получают первым параметром экземпляр класса, а статические не получают ничего, в классовые методы передается класс. Возможность создания классовых методов является одним из следствий того, что в Python классы также являются объектами. Для создания классового (только «новые» классы могут иметь классовые методы) метода можно использовать декоратор
classmethod>>> class A(object): def __init__(self, int_val): self.val = int_val + 1 @classmethod def fromString(cls, val): # вместо self принято использовать cls return cls(int(val)) ... >>> class B(A):pass ... >>> x = A.fromString("1") >>> print x.__class__.__name__ A >>> x = B.fromString("1") >>> print x.__class__.__name__ B
Классовые методы достаточно часто используются для перегрузки конструктора. Классовые методы, как и статические, реализуются через свойства (property).
Мультиметоды
Примером для иллюстрации сути мультиметода может служить функция
add()из модуляoperator:>>> import operator as op >>> print op.add(2, 2), op.add(2.0, 2), op.add(2, 2.0), op.add(2j, 2) 4 4.0 4.0 (2+2j)
В языке Python достаточно легко реализовать и определённые пользователем мультиметоды[9]. Например, эмулировать мультиметоды можно с помощью модуля multimethods.py (из Gnosis Utils) :
from multimethods import Dispatch class Asteroid(object): pass class Spaceship(object): pass def asteroid_with_spaceship(a1, s1): print "A-><-S" def asteroid_with_asteroid(a1, a2): print "A-><-A" def spaceship_with_spaceship(s1, s2): print "S-><-S" collide = Dispatch() collide.add_rule((Asteroid, Spaceship), asteroid_with_spaceship) collide.add_rule((Asteroid, Asteroid), asteroid_with_asteroid) collide.add_rule((Spaceship, Spaceship), spaceship_with_spaceship) collide.add_rule((Spaceship, Asteroid), lambda x,y: asteroid_with_spaceship(y,x)) a, s1, s2 = Asteroid(), Spaceship(), Spaceship() collision1 = collide(a, s1)[0] collision2 = collide(s1, s2)[0]
Устойчивость объектов
Объекты всегда имеют своё представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или передавать их на другие компьютеры. Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов (сериализацией) в виде байтовых последовательностей и их последующего восстановления (десериализация).
Модуль
pickleявляется наиболее простым способом «консервирования» объектов в Python.Следующий пример показывает как работает сериализация и десериализация:
# сериализация >>> import pickle >>> p = set([1, 2, 3, 5, 8]) >>> pickle.dumps(p) 'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' # де-сериализация >>> import pickle >>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') >>> print p set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже — прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных источников с помощью модуля
pickle, так как при этом возможны практически любые действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам или с ненадежными источниками можно воспользоваться другими модулями для сериализации.В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта — это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы
__getstate__,__setstate__и др.).На стандартном для Python механизме сериализации построена работа модуля
shelve(shelve(англ. глаг.) — ставить на полку; сдавать в архив). Модуль предоставляет функциюopen. Объект, который она возвращает, работает аналогично словарю, но объекты сериализуются и сохраняются в файле:>>> import shelve >>> s = shelve.open("myshelve.bin") >>> s['abc'] = [1, 2, 3] >>> s.close() # ..... >>> s = shelve.open("myshelve.bin") >>> s['abc'] [1, 2, 3]
Сериализация
pickle— не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML.Примечания
- ↑ Dr. Dobb’s Journal, November, 1997
- ↑ Introduction to Object-Oriented Programming
- ↑ в списке рассылки comp.lang.python
- ↑ «AKO» и «IS-A»
- ↑ Beazley, 2009
- ↑ How-To Guide for Descriptors by R. Hettinger
- ↑ New-style Classes
- ↑ Объяснение Гвидо ван Россума об объединении типов и классов
- ↑ Charming Python: Multiple dispatch
Литература
- David M. Beazley Python Essential Reference. — 4th Edition. — Addison-Wesley Professional, 2009. — 717 с. — ISBN 978-0672329784
Ссылки
Для улучшения этой статьи желательно?: - Проставив сноски, внести более точные указания на источники.
- Обновить статью, актуализировать данные.
Категории:- Объектно-ориентированное программирование
- Python
Wikimedia Foundation. 2010.