Comments 29
Очень интересно, спасибо; а можно добавить свои 5 копеек? )

>>… классический ООП подразумевает наличие только классов и объектов
О каком классическом ООП тут идет речь? :) Дело в том, что классический ООП — это, скорее Smaltalk, а там наличествуют и классы классов, и MetaObject Protocol, аналоги которого есть в Python и Ruby. Тот ООП, в котором этого нет — скорее, mainstream-ООП: c++, java, c#.

Ну… Smalltalk он, конечно, стар, крут и прапапа всего сущего, но все же ООП вошел в мэйнстрим благодаря прежде всего C++/Java, не так ли?

Поэтому на них и захотелось сослаться.
Не вопрос :) Хотел уточнить, чтобы убедиться, что я все правильно понял ) Спасибо за ответ.
Я пайтон почти не знаю но разве что немного синтаксис но блин все больше удивляюсь его возможностям, прям взрыв мозгов %) Жду еще подобных статей
UFO landed and left these words here
про метаклассы есть большая куча статей в гугле только они все старые… и специфические очень.
а тут наконец-то что-то собирающее всё в одной куче. респект автору.
>Питон расширяет классическую парадигму, и сами классы в нем тоже становятся
равноправными объектами

Т.е. с метаклассами классы становятся первоклассным объектом в языке?
Бинго.

Более того, и сами метаклассы тоже являются первоклассным объектом Питона, их можно передавать куда угодно, добавлять атрибуты и методы и проводить любые действия, возможные для объектов.
>Более того, и сами метаклассы тоже являются первоклассным объектом Питона, их можно передавать куда угодно, добавлять атрибуты и методы и проводить любые действия, возможные для объектов.

Собсно, это и подразумевается под «первоклассностью».
«Объект конструируется вызовом класса оператором „()“. Создадим наследованием от
type метакласс, переопределяющий этот оператор:»

А зачем такое извращение? вызов на классе () это банальный вызов конструктора у type. Поэтому если уж вы наследуетесь от type переопределять нужно имхо __init__, а никак не __call__.

Другое дело что в качестве __metaclass__ совершенно необязательно присваивать наследника type, а можно использовать любую callable штуку. Так что если бы у вас был например class AttributeInitType(object): ваша конструкция имела бы смысл.

Или я ошибаюсь?
Ошибаетесь. Не проверял на Python 3.0, но на моем Python 2.6.2 получится вот так:

    class AttributeInitType(object):
        def __call__(self, *args, **kwargs):
            """ Вызов класса создает новый объект. """
            # Перво-наперво создадим сам объект...
            obj = type.__call__(self, *args)
            # ...и добавим ему переданные в вызове аргументы в качестве атрибутов.
            for name in kwargs:
                setattr(obj, name, kwargs[name])
            # вернем готовый объект
            return obj
    
    class Man(object):
        __metaclass__ = AttributeInitType
    
    me = Man(height = 180, weigth = 80)
    
    print me.height

>>> Traceback (most recent call last):
  File "/tmp/py7637jbF", line 13, in <module>
    class Man(object):
TypeError: Error when calling the metaclass bases
    object.__new__() takes no parameters


Почему же переопределялся __call__? потому что __init__ вызывается при определении класса, а не появлении объекта. Только что все проверил лично.
очень в тему, знаком со статьей. Одно из лучших пояснений к системе типов Питонаю
Очень круто! Спасибо за статью. И раз уж Вы здесь и знакомы с Django, как достигается цепочка параметров при запросе — Model.objects.filter().order_by().values() и т.д.?
Имеем, например:

  class Apple(Model):
      pass


Можем, например, сделать так:

  Apple.objects.all()  


Так вот. objects — ссылка на экземпляр класса Manager. В этом классе собрана вся
логика работы с моделью, стандартная либо расширенная для данной конкретной модели. По
любому из его методов вроде all() или get() возращается объект QuerySet,
инкапсулирующий в себе информацию об отложенных запросах.

Методы all() и get_query_set() из класса Manager:

  def get_query_set(self):
      """Returns a new QuerySet object.  Subclasses can override this method
      to easily customize the behavior of the Manager.
      """
      return QuerySet(self.model)

  def all(self):
      return self.get_query_set()


Теперь заглянем в определение класса QuerySet():

  def all(self):
      """
      Returns a new QuerySet that is a copy of the current one. This allows a
      QuerySet to proxy for a model manager in some cases.
      """
      return self._clone()

  def filter(self, *args, **kwargs):
      """
      Returns a new QuerySet instance with the args ANDed to the existing
      set.
      """
      return self._filter_or_exclude(False, *args, **kwargs)

  def _filter_or_exclude(self, negate, *args, **kwargs):
      if args or kwargs:
          assert self.query.can_filter(), \
                  "Cannot filter a query once a slice has been taken."

      clone = self._clone()
      if negate:
          clone.query.add_q(~Q(*args, **kwargs))
      else:
          clone.query.add_q(Q(*args, **kwargs))
      return clone


Что делают эти методы? Они создают копию последнего QuerySet, дописывают в атрибут
self.query новую, условного говоря, строчку, и возращают после этого копию.

Легко получается Apple.object.all().filter(color=«green»).get(). На каждом шаге,
в каждой точке, происходит копирование «наброска» запроса с расширением и
возвратом копии.

Так, кстати, и получаются эти самые «ленивые», «отложенные» вычисления. Запросы
к базе, скажем так, записываются в строчку, а выполняются уже одним пакетом либо
в уже шаблоне, либо по желанию программиста — преобразованием к обычному списку
объектов.
>Но если класс — объект, то какому классу он соответствует?
>По умолчанию этот класс (метакласс) называется type.
Задам дурацкий вопрос, а какой метакласс у type?
<a href«www.cafepy.com/article/python_types_and_objects/»> ссылочка по теме, где очень внятно нарисовано и объяснено. Тут выше ее, кажется, уже приводили.

Оттуда или прямо из интерпретатора видим, что,
>>>type(type)
<type 'type'>


И еще экспериментрируем:
>>> isinstance(object, type)
True
>>> isinstance(type, object)
True
>>> 


Ну так вот. type является «конкретизацией», или экземпляром, класса type(как бы рекурсивно) и наследует от object.

В то же время класс object является экземпляром класса type, но не наследует ни от чего.

Главное здесь:
1) Все, и классы, и метаклассы — наследуют от object. Значит, они являются первоклассными объектами.

2) Поведение класса в моменты объявления, вызова и наследования редактируется в метаклассе.

3) Поведение объектов редактируется в классе.
Бляха… Не пойму, что тут с тегом творится. Или ошибся сам где-то?
Итак, классический ООП подразумевает наличие только классов и объектов.

В ООП достаточно объектов и типов. Оффтопичный Javascript обходится лишь этим. Я не спорю. В этой области с терминологией творится полный кошмар.

Как показано на картинке по ссылке выше, пунктирчато-стрелочное отношение instanceOf транзитивно. Но, тем не менее, у каждого объекта есть один единственный, самый близкий ему тип. Тот, который его породил.

В Javascript отношение между объектом и «самым близким типом» реализуется через неявную ссылку obj.[[Prototype]], который можно выдернуть, сказав obj.constructor. В python — через obj.__class__ (который, не смотря на название, содержит-таки ссылку на этот «самый близкий тип»). Это я к тому, что термины: constructor и __class__ это синонимы. Поэтому, когда речь заходит об объекте, создающим объекты, то его стоит называть конструктором. Правда это не то же самое, что __init__(self), которая всего лишь процедура инициализации, связанная с объектом (она ни чего не создает, ей объект прилетает уже созданным, в самом первом аргументе self :) ).

В принципе, ссылки от объекта к своему конструктору (который тоже объект и у которого есть свой конструктор, котор…) уже достаточно для того, чтобы порождать магию instanceOf (то бишь typeof), и говорить о наследовании. Т.е. если представить, что у питоньего объекта есть лишь __class__ и __dict__, то станет понятно как живется несчастному жаваскрипту, без классов. :)

Но, в самом деле, как же «класс-ическое» ООП без классов? :) Class это обычный конструктор, характерной чертой которого является поддержка пусто-стрельчатого отношения IsA (т.е. наследования как показано на картинке) между объектами. Для этого в питоньих классах и появляется атрибут __bases__, где хранится кортеж супер-классов (список непосредственных родителей для данного класса, в смысле), суровое шаманство для иерархического поиска атрибутов в этой куче (в дополнение к поиску по иерархии типов), логика instanceOf начинает использовать isA, и определенные правила для манипуляции с object.__dict__. Мрак.

Для того, чтобы создавать классы «на лету» не обязательно придумывать новые конструкторы (мета-классы). Python предоставляет несколько встроенных и шикарную библиотеку (import types) на все оставшиеся 90% случаев жизни. Например:
>>> ClassOfPythons = type('ThePython', (object,), {'voice': 'sh-sh-sshhhhhh....'})
>>> squasher = ClassOfPythons()
>>> squasher.voice
sh-sh-sshhhhhh....

ну или так:
ClassOfPythons = object.__class__('ThePython', (object,), {'voice': 'sh-sh-sshhhhhh....'})

что абсолютно тоже самое, поскольку id(object.__class__) == id(type)

Мета-класс это узко-специальная магия для того, чтобы класс мог выбрать себе конструктора. Фактически это ситуация равносильна тому, когда хвост станет махать собакой — бессмыслица, но иногда очень нужно. :) В метакласс в django-модели используется сугубо как синтаксический сахар. Пол-статьи ни о чем. А вот пример с абстрактным классом, просто красавчик. За это даже плюсик в карму, если получится. :)
Очень люблю Javascript за его подход к ООП. Очень. Но не понимаю, вообще не понимаю, при чем здесь язык :)

Приятно, конечно, что прототипы позволяют избавиться от лишнего слоя абстракций в виде классов и, тем более, метаклассов. Более того, эти сущности при желании можно моделировать средствами JS.

Но все же людям привычно классифицировать понятия, которыми оперирует система; оттуда и страсть, ИМХО, к этому стилю (есть класс и его объект) во множестве популярнейших языков. С такой аргментации начинается среднее введение в ООП.

зачем вы привели сейчас либу types? Цитирую справку: «This module defines names for some object types that are used by the standard Python interpreter...» То есть можно будет проверять, правильные ли типы подсунуты на вход функции. Пример с того же дока:

from types import *
def delete(mylist, item):
    if type(item) is IntType:
       del mylist[item]
    else:
       mylist.remove(item)


Метаклассы — тонкий инструмент, использовать в большей части случаев не обязательно, даже избыточно. Но иной раз он позволяет изящно и красиво решать уникальные задачи. Типа DSL склепать в стиле джанговских моделей.

Очень хорошо представляю, как вы начнете всю эту логику наследования моделей, передачи атрибута _meta, учет абстрактных базовых классов решать в функциональном стиле (ModelClass = type (...))! Будет и больше, и мутнее, и некрасиво.

Дело, конечно, хозяйское, Питон дает несколько путей решения этих проблем. Насколько я понимаю слово pythonic — лучшее решение то, которое легче понять и прочитать.
Приятно, конечно, что прототипы позволяют избавиться от лишнего слоя абстракций в виде классов и, тем более, метаклассов. Более того, эти сущности при желании можно моделировать средствами JS.

Извиняюсь за сумбурность вышесказанного. Ведь хотелось подвести к прямо противоположному выводу. :) Объекты и типы — вот базовая штука в ООП. А классы — это такой жутко полезный тип, — которого в javascript, как раз, серьезно не хватает. Ну и они там не реализуется по-человечески, поскольку язык не позволяет абстрагировать основную оопшную точкозапись «obj.property».
Поясните фразу «не позволяет абстрагировать». Через точку можно обращаться к свойствам объекта-словаря? Можно. В чем проблема?

У меня до сих пор не стояла задача масштабного проектирования на JS, средняя задача на этом языке гораздо меньше. Но интересно было бы выработать какой-нибудь специфичный для прототипной схемы наследования подход в архитектуре.

Я пока не видел никаких особых руководств на эту тему… Надо поискать да подумать.
Вы пишете obj.property, явно желая получить о объекта «obj», нечто, связанное с именем «property». Но где объект возьмет это «нечто»? В конструкторе? В __dict__? В каком-то из супер-классов? Для этого, в new-классах python используются умопомрачительные правила: www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html#id402018 (следующая статья по вашей ссылке :) ). Другие типы (не-new-классы) имеют другие правила. А благодаря «магическим» методам (__getattr__, __setattr__ и т.п.) и вы можете придумать что-то свое. Но, что бы вы там не напридумывали, «снаружи» это все равно будет выглядить как «obj.property». Вот это и есть абстракция доступа.

В Javascript можно реализовать подобные правила, и описать их внутри методов. Но тогда и «снаружи» они будут выглядить как вызовы методов. Мне попадалось, например, такое: value = obj.findName('property').get(). Внутри findName() реализован механизм ресолвинга свойства по иерархии классов, а при помощи get() абстрагирован доступ к value. Т.е. логически все сделано. Но без абстракции доступа (синтаксического сахара, со стороны языка, если хотите).
Ну понятно…

Никто, в общем-то, и не говорил, что js — пирамида Хеопса компьютерного языкостроения, да и слава богу.

В любом случае, некоторые особенности языка не могут не вызвать симпатии. Другие же — недоумение от ограниченности. :)
Логика диспетчеризации запроса для получения свойства с использованием прототипа, примитивна до безобразия:
— Эй, Объект, гони сюда свойство property!
— Хм… а нету… (если есть, был обязан выдать)
— Тогда пусть вернет тот, кто тебя породил, такого урода! (объект бежит просить в словаре у своего конструктора)

Если и у конструктора такого свойства нет, то обычно, на этом все и заканчивается, ибо конструктор конструктора уже не при делах: «вассал моего вассала не мой вассал». Это все, что доступно в JS.

Диспетчеризация запроса по иерархии классов отражает совсем другое отношение: конкретное-общее (класс -> супер-класс) — и логика диспетчеризации запросов тут совершенно иная. Единственное что их роднит с «объект-конструктор», это транзитивное отношение «instanceOf», которое может означать и «объект типа», и «объект класса» (см. диаграмму www.cafepy.com/article/python_types_and_objects/images/relationships_transitivity.png). Но из-за этого «двойного» смысла и возникает путаница, будто одно можно выразить через другое. Это не так (не даром, на диаграмме стрелки выглядят по разному).
Only those users with full accounts are able to leave comments. Log in, please.