Что такое 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!