Что такое Ruby-аналог Python Metlasslasses?

Python имеет представление о метаклассах, которые, если я правильно понимаю, позволяют вам модифицировать объект класса в момент построения. Вы не изменяете класс, а вместо этого объект, который должен быть создан, инициализируется.

Python (по крайней мере, по состоянию на 3.0, я считаю) также имеет идею декораторов класса. Опять же, если я правильно понимаю, декораторы классов допускают модификацию определения класса в момент его объявления.

Теперь я считаю, что в Ruby есть декоративная функция или функция, эквивалентная функции, но в настоящее время я не знаю о чем-то эквивалентном метаклассам. Я уверен, что вы можете легко прокачать любой объект Ruby с помощью некоторых функций и делать то, что хотите, но есть ли функция на языке, который устанавливает это как метаклассы?

Итак, имеет ли Ruby нечто похожее на метаклассы Python?

Изменить. Я был отключен в метаклассах для Python. Метаклас и декоратор класса делают очень похожие вещи, которые появляются. Они изменяют класс, когда он определяется, но по-разному. Надеюсь, гуру Python придет и лучше объяснит эти функции в Python.

Но класс или родительский класс могут реализовать функцию __new__(cls[,..]), которая настраивает конструкцию объекта до его инициализации с помощью __init__(self[,..]).

Изменить Этот вопрос в основном предназначен для обсуждения и изучения того, как эти два языка сравниваются в этих функциях. Я знаком с Python, но не с Ruby, и мне было любопытно. Надеюсь, кто-нибудь, у кого есть тот же вопрос о двух языках, найдет это сообщение полезным и просвещенным.

Ответ 1

В Ruby нет метаклассов. В Ruby есть некоторые конструкции, которые некоторые люди иногда ошибочно называют метаклассами, но они не являются (что является источником бесконечной путаницы).

Однако есть много способов добиться тех же результатов в Ruby, что и с метаклассами. Но, не сказав нам, что именно вы хотите сделать, не известно, какими могут быть эти механизмы.

Короче:

  • Ruby не имеет метаклассов
  • Ruby не имеет ни одной конструкции, которая соответствует метаклассам Python
  • Все, что Python может делать с метаклассами, также можно выполнить в Ruby
  • Но нет единой конструкции, вы будете использовать разные конструкции в зависимости от того, что именно вы хотите сделать
  • Любая из этих конструкций, вероятно, имеет и другие функции, которые не соответствуют метаклассам (хотя они, вероятно, соответствуют чему-то еще в Python)
  • Пока вы можете делать что-либо в Ruby, что вы можете делать с метаклассами на Python, это может быть не просто.
  • Хотя часто будет более гибкое решение Rubyish
  • И последнее, но не менее важное: пока вы можете делать что-либо в Ruby, что вы можете делать с метаклассами на Python, это может быть не обязательно Ruby Way.

Итак, что такое метаклассы? Ну, это классы классов. Итак, давайте сделаем шаг назад: что такое классы?

Классы & hellip;

  • являются фабриками для объектов
  • определить поведение объектов
  • определить на метафизическом уровне, что значит быть экземпляром класса

Например, класс Array создает объекты массива, определяет поведение массивов и определяет, что означает "array-ness".

Назад в метаклассы.

Метаклассы & hellip;

  • являются фабриками для классов
  • определяет поведение классов
  • определить на метафизическом уровне, что значит быть классом

В Ruby эти три обязанности разделены на три разных места:

  • класс Class создает классы и определяет немного поведения
  • индивидуальный класс eigenclass определяет немного поведения класса
  • понятие "классовость" привязано к интерпретатору, что также реализует основную часть поведения (например, вы не можете наследовать от Class, чтобы создать новый класс, который ищет методы по-разному или что-то вроде что – алгоритм поиска метода привязан к интерпретатору)

Итак, эти три вещи вместе играют роль метаклассов, но ни один из них не является метаклассом (каждый из них реализует только небольшую часть того, что делает метакласс), и не является суммой этих метаклассов (потому что они делают намного больше).

К сожалению, некоторые люди называют eagenclasses классов метаклассами. (До недавнего времени я был одной из тех заблудших душ, пока я наконец не увидел свет.) Другие люди называют все метаэкраны eigenclasses. (К сожалению, один из этих людей является автором одного из самых популярных руководств по метапрограммированию Ruby и объектной модели Ruby.) Некоторые популярные библиотеки добавляют метод metaclass к Object, который возвращает объект eigenclass (например, ActiveSupport, Facets, metaid). Некоторые люди называют все классы виртуальных классов (например, eigenclasses и include classes). Некоторые люди называют Class метаклассом. Даже в самом исходном коде Ruby слово "metaclass" используется для обозначения вещей, которые не являются метаклассами.

Ответ 2

Ваш обновленный вопрос теперь выглядит совсем по-другому. Если я правильно вас понимаю, вы хотите подключиться к распределению объектов и инициализации, что абсолютно не имеет никакого отношения к метаклассам. (Но вы все еще не пишете, что именно вы на самом деле хотите сделать, поэтому я все равно могу отключиться.)

В некоторых объектно-ориентированных языках объекты создаются конструкторами. Однако у Ruby нет конструкторов. Конструкторы - это всего лишь методы factory (с глупыми ограничениями); нет причин иметь их на хорошо продуманном языке, если вы можете просто использовать (более мощный) factory метод.

Конструкция объекта в Ruby работает следующим образом: построение объекта разделяется на две фазы, распределение и инициализацию. Выделение осуществляется методом открытого класса с именем allocate, который определяется как метод экземпляра класса Class и, как правило, никогда не переоценивается. (На самом деле, я не думаю, что вы действительно можете переопределить его.) Он просто выделяет пространство памяти для объекта и устанавливает несколько указателей, однако на данный момент объект не используется.

То, в которое входит инициализатор: это метод экземпляра, называемый initialize, который устанавливает внутреннее состояние объекта и переносит его в согласованное, полностью определенное состояние, которое может использоваться другими объектами.

Итак, чтобы полностью создать новый объект, вам нужно сделать следующее:

x = X.allocate
x.initialize

[Примечание: Objective-C программисты могут это распознать.]

Однако, поскольку слишком легко забыть вызывать initialize, и, как правило, объект должен быть полностью корректным после построения, существует удобный метод factory, называемый Class#new, который делает все, что работает для вы и выглядите примерно так:

class Class
  def new(*args, &block)
    obj = allocate
    obj.initialize(*args, &block)

    return obj
  end
end

[Примечание: на самом деле initialize является закрытым, поэтому для обхода ограничений доступа необходимо использовать отражение: obj.send(:initialize, *args, &block)]

Это, кстати, причина, по которой вы создаете объект, который вы вызываете методом открытого класса Foo.new, но вы реализуете метод частного экземпляра Foo#initialize, который, похоже, вызывает много новых новичков.

Однако ничто из этого никак не испечено на языке. Тот факт, что основной метод factory для любого класса обычно называется new, является просто соглашением (и иногда мне хотелось бы, чтобы он был другим, потому что он похож на конструкторы в Java, но совершенно другой). В других языках конструктор должен иметь определенное имя. В Java он должен иметь то же имя, что и класс, что означает, что a) может быть только один конструктор, и b) анонимные классы не могут иметь конструкторы, потому что у них нет имен. В Python метод factory должен быть вызван __new__, что опять же означает, что может быть только один. (Как в Java, так и в Python, вы можете, конечно, иметь разные методы factory, но их вызов выглядит отличным от вызова по умолчанию, в то время как в Ruby (и Smalltalk, откуда этот шаблон возник) он выглядит одинаково.)

В Ruby может быть как можно больше методов factory с любым именем, которое вам нравится, а метод factory может иметь много разных имен. (Для классов сбора, например, метод factory часто присваивается псевдониму [], что позволяет вам писать List[1, 2, 3] вместо List.new(1, 2, 3), который заканчивается, скорее похожий на массив, тем самым подчеркивая коллективный характер списки.)

Короче:

  • стандартизованный метод factory Foo.new, но он может быть любым
  • Foo.new вызывает allocate для выделения памяти для пустого объекта foo
  • Foo.new затем вызывает foo.initialize, то есть метод экземпляра Foo#initialize
  • все три из них - это просто такие методы, как любые другие, которые вы можете определить, переопределить, переопределить, обернуть, псевдоним и многое другое
  • ну, кроме allocate, которому необходимо выделить память внутри исполняемой среды Ruby, которую вы действительно не можете сделать из Ruby

В Python __new__ примерно соответствует как new, так и allocate в Ruby, а __init__ точно соответствует initialize в Ruby. Основное различие заключается в том, что в Ruby new вызывает initialize, тогда как в Python среда выполнения автоматически вызывает __init__ после __new__.

Например, вот класс, который разрешает создавать не более двух экземпляров:

class Foo
  def self.new(*args, &block)
    @instances ||= 0
    raise 'Too many instances!' if @instances >= 2

    obj = allocate
    obj.send(:initialize, *args, &block)

    @instances += 1

    return obj
  end

  attr_reader :name

  def initialize(name)
    @name = name
  end
end

one = Foo.new('#1')
two = Foo.new('#2')
puts two.name         # => #2
three = Foo.new('#3') # => RuntimeError: Too many instances!