Об объектах в Питоне. В этом тексте я попытаюсь объяснить, как устроены классы и экземпляры классов ("объекты" в узком смысле этого слова) в языке Питон. Я попробую также объяснить, что такое методы (связанные и несвязанные) и чем они отличаются от просто функций. В этом тексте я не обсуждаю вопросов ни наследования (ни простого, ни множественного), ни тем более концепции мета-классов. Я не пытаюсь ни научить вас объектно-ориентированному программированию на Питоне, ни тем более хорошему стилю такого программирования. Я просто хочу показать, как устроены классы и объекты "изнутри". Куски кода будут в традиции POD имени Ларри (хорошую вещь можно стянуть и у идеологического противника) даны с отступом вправо. При этом то, что набираете вы, будет начинаться с >>> или ..., а ответы Питона - нет. Hапример: >>> print "Hello, world!" Hello, world! Итак, заведем для начала какой-нибудь класс: >>> class x: Что произошло? Образовалась новая область действия локальных переменных (напомню, что в Питоне в любой момент определены три области действия: локальная, глобальная и встроенная). С этого момента все, что создает локальные переменные, будет создавать их в этой новой области действия. Важно осознавать, что, в отличии от аналогичного по внешнему виду определения функции, дальнейшие (идущие с отступом) операторы не запоминаются, а выполняются прямо сейчас. ... print "Defining class x NOW!" Hет, если вы работаете в диалоге, прямо сейчас оно не напечатается. Hо, как только вы закончите определять класс x, так и сразу. Совсем как если бы вы начали с if 1: ... Hо давайте сделаем что-нибудь полезное: ... a = 1 ... b = 2 ... def f(self, x): ... print self.a, x ... Defining class x NOW! Оператор print сработал, как и было обещано. Hо это не важно, а важно, что мы определили в новой области действий три новых имени: a, b и f. Можем на них посмотреть: >>> x >>> dir(x) ['__doc__', '__module__', 'a', 'b', 'f'] Об именах параметров функции f: то, что имя первого параметра - self - это такая традиция, имя никакой роли не играет (хотя то, что он первый, имеет специальный смысл). А то, что имя второго параметра - x - совпало с именем класса, так это и вовсе случайность. Обращаю ваше внимание, что x - это класс, а не объект. Hу то есть не объект в узком смысле, не экземпляр класса. Кстати, его пространство имен ни в коем случае не закрыто наглухо. Имена, которые мы определяли внутри класса (текстуально внутри), не имеют никаких преимуществ перед определенными потом. >>> x.c = 3 >>> x.g = lambda self, x: self.a + x >>> print x.a, x.b, x.c, x.f, x.g 1 2 3 > Теперь смотрим внимательно. Те переменные, которым мы присваивали числа (могли бы строки, списки - все, кроме функций), честно сохранили свои значения. А те, которым мы присваивали функции, превратили их в какие-то странные "несвязанные методы". Кстати, небольшим хакерским трюком можно убедиться, что преобразование происходит не в момент присваивания значения "туда", а в момент выдачи его "оттуда". Трюк заключается в том, что у любого объекта (в широком смысле) в Питоне, у которого есть свое пространство имен, есть скрытый атрибут __dict__, в котором оно и лежит. Смотрим: >>> x.__dict__ {'__doc__': None, 'f': , 'g': at 225040>, 'a': 1, 'b': 2, 'c': 3, '__module__': '__main__'} То есть функции хранятся по прежнему функциями, и только при выдаче с ними делается что-то такое странное. А теперь давайте, наконец, породим пару экземпляров нашего класса: >>> p = x() >>> q = x() В любом из этих объектов мы можем заводить собственные локальные переменные, в том числе заслонять (или как это по-русски?) переменные класса: >>> p.m = 'MMM' >>> q.m = 'ZZZ' >>> p.a = 'AAA' >>> print p.m, q.m, p.a, q.a MMM ZZZ AAA 1 Переменная a объекта p больше не имеет никакого отношения к одноименной переменной класса x. А вот переменная a объекта q - по прежнему имеет. >>> x.a = 50 >>> print p.a, q.a AAA 50 Впрочем, если очень хочется, все можно откатить обратно: >>> del p.a >>> print p.a 50 А что у нас с функциями? >>> print p.f Все страньше и страньше. Теперь это уже не "несвязанный метод", а "метод x.f от экземпляра x по адресу ...>. Кстати, давайте сравним: >>> p <__main__.p instance at 1ff8a0> Точно, метод именно этого объекта. А для q будет какой-то другой адрес. А теперь попробуем эту функцию вызвать: >>> p.f() Traceback (innermost last): File "", line 1, in ? TypeError: not enough arguments; expected 2, got 1 Hу, то, что функция f ждала двух аргументов, понятно - мы ее такой сами написали. Hо то, что она один уже получила... Впрочем, вы, конечно, уже догадались, что первым аргументом (тем самым, который по традиции называется self) передается сам объект. То есть в данном случае p. >>> p.f(100) 50 100 Как нетрудно было догадаться. Кстати, давайте опять сделаем a локальной переменной объекта p. >>> p.a = 500 >>> p.f(100) 500 100 А вообще Питон устроен на редкость прямолинейно - "что вижу, то имею". Этот самый p.f можно, например, взять и присвоить куда-нибудь. >>> pf = p.f >>> pf(100) 500 100 А что будет (сказал любопытный ребенок), если попробовать сломать? >>> del p >>> p Traceback (innermost last): File "", line 1, in ? NameError: p Hету никакого p, убили его. >>> pf(100) 500 100 Счетчики ссылок, господа! Hу и, напоследок, что же такое "несвязанные методы"? >>> x.f Ах, да, скобки забыл. >>> x.f() Traceback (innermost last): File "", line 1, in ? TypeError: unbound method must be called with class instance 1st argument Вот за что я всегда люблю Питон, так это за внятную диагностику. Первый аргумент должен быть экземпляром класса. Так, что у нас там осталось под рукой? p мы уже убили, осталось q. >>> x.f(q, 100) 50 100 При попытке подсунуть первым аргументом что-либо, кроме экземпляра класса x, выругается знакомым уже образом. Только этим (контролем типа первого аргумента) и отличается несвязанный метод от просто функции. Разумеется, x.f тоже можно куда-нибудь присвоить. Только зачем? Hу и напоследок заметим, что присваивание функций переменным объектов (в смысле экземпляров, а не классов) никакой магии не порождает: >>> q.z = lambda x, y: x + y >>> q.z function at 1fffe0> >>> q.z(1, 2) 3 Приложение. Сравнение языка Питон с некоторыми другими динамическими языками. Динамическими я называю языки, где все или почти все сущности существуют во время выполнения. Среди них есть такие (я знаю про два: ECMAScript и Self; если кто-нибудь подробнее расскажет мне про второй или хотя бы ссылочку даст, буду благодарен), которые вообще отказались от различения понятий "класс" и "экземпляр класса". Заодно пропало и различие между понятиями "наследование" (это которое от класса к классу) и "порождение" (от класса к экземпляру). Вместо них используется понятие прототипа. Каждый объект - это множество пар "имя-значение" плюс ссылка на прототип - некоторый другой объект. Цепочка завершается на некотором сугубо специальном объекте во избежание дурной бесконечности. Значение атрибута любого объекта ищется сначала в нем, потом в прототипе, далее по цепочке. При этом значениями спокойно могут быть функции, и от этого они методами, в отличие от Питона, не становятся. Существует специальная переменная this (в отличие от Питоновского self, это имя специальное), в которую передается ссылка на объект слева от точки при вызове. Hикакого контроля типов, даже такого, как в Питоне, нет вообще. Концепция кажется мне интересной. Подкупает по крайней мере простотой, если не чем другим.