Python имеет идею метаклассов, которые, если я понимаю правильно, позволяют Вам изменять объект класса во время конструкции. Вы не изменяете класс, но вместо этого объект, который должен быть создан затем инициализированный.
Python (по крайней мере, с 3,0 я верю), также имеет идею декораторов класса. Снова, если я понимаю правильно, декораторы класса позволяют изменение определения класса в данный момент, оно объявляется.
Теперь я полагаю, что существует эквивалентная функция или функции декоратору класса в Ruby, но я в настоящее время не знаю о чем-то эквивалентном метаклассам. Я уверен, что можно легко накачать какой-либо объект Ruby через некоторые функции и сделать то, что Вы будете к нему, но существует ли функция на языке, который настраивает, это как метаклассы делает?
Таким образом, снова Ruby имеет что-то подобным метаклассам Python?
Редактирование я был выключен на метаклассах для Python. Метакласс и декоратор класса делают очень похожие вещи, это появляется. Они оба изменяют класс, когда он определяется, но различными способами. Надо надеяться, гуру Python войдет и объяснит лучше на этих функциях в Python.
Но класс или родитель класса могут реализовать a __new__(cls[,..])
функция, которая действительно настраивает конструкцию объекта, прежде чем это будет инициализировано с __init__(self[,..])
.
Редактирование, которое Этот вопрос главным образом для обсуждения и приобретения знаний о том, как эти два языка выдерживают сравнение в этих функциях. Я знаком с Python, но не Ruby и был любопытен. Надо надеяться, кто-либо еще, у кого есть тот же вопрос об этих двух языках, найдет это сообщение полезным и поучительным.
В Ruby нет метаклассов. В Ruby есть некоторые конструкции, которые некоторые люди иногда ошибочно называют метаклассами, но это не так (что является источником бесконечной путаницы).
Однако есть много способов добиться в Ruby тех же результатов, что и с метаклассами. Но не сообщая нам, что именно вы хотите сделать, невозможно сказать, что это за механизмы.
Вкратце:
Итак, что точно метаклассы? Ну, это классы классов. Итак, давайте сделаем шаг назад: что такое классы ?
Классы…
Например, класс Array
создает объекты массива, определяет поведение массивов и определяет, что означает «массивность».
Вернемся к метаклассам.
Метаклассы…
В Ruby эти три обязанности разделены на три разных места :
Class
создает классы и определяет немного поведения Class
, чтобы создать новый тип класса, который ищет методы по-другому, или что-то в этом роде - алгоритм поиска метода встроен в интерпретатор) Итак, эти три вещи вместе играют роль метаклассов, но ни один из них не является метаклассом (каждый реализует лишь небольшую часть того, что делает метакласс), ни сумма этих метаклассов (потому что они много больше чем это).
К сожалению, некоторые люди называют собственные классы классов метаклассами. (До недавнего времени я был одной из тех заблудших душ, пока наконец не увидел свет.) Другие люди называют все собственными классами метаклассами. (К сожалению, один из этих людей является автором одного из самых популярных руководств по метапрограммированию Ruby и объектной модели Ruby.) Некоторые популярные библиотеки добавляют метод метакласса
к Object
, который возвращает значение собственный класс объекта (например, ActiveSupport, Facets, metaid). Некоторые люди называют все виртуальными классами (т.е.eigenclasses и include classes) метаклассы. Некоторые называют Class
метаклассом. Даже в самом исходном коде Ruby слово «метакласс» используется для обозначения вещей, которые не являются метаклассами.
Ваш обновленный вопрос теперь выглядит иначе. Если я вас правильно понял, вы хотите подключиться к распределению и инициализации объектов, что не имеет абсолютно ничего общего с метаклассами. (Но вы по-прежнему не пишете, что вы действительно хотите делать, так что я все равно могу отключиться.)
В некоторых объектно-ориентированных языках объекты создаются конструкторами. Однако в Ruby нет конструкторов. Конструкторы - это всего лишь фабричные методы (с дурацкими ограничениями); нет причин использовать их на хорошо разработанном языке, если вместо этого вы можете просто использовать (более мощный) фабричный метод.
Создание объекта в Ruby работает следующим образом: построение объекта делится на две фазы: размещение и инициализация . Распределение выполняется общедоступным методом класса allocate
, который определяется как метод экземпляра класса Class
и обычно никогда не переопределяется. (На самом деле, я не думаю, что вы на самом деле можете переопределить его.) Он просто выделяет пространство памяти для объекта и устанавливает несколько указателей, однако объект на данный момент не может использоваться.
Здесь на помощь приходит инициализатор: это метод экземпляра с именем initialize
, который устанавливает внутреннее состояние объекта и переводит его в согласованное, полностью определенное состояние, которое может использоваться другими объектами.
Итак, чтобы полностью создать новый объект, вам нужно сделать следующее:
x = X.allocate
x.initialize
[Примечание: программисты Objective-C могут это понять.]
Однако, поскольку слишком легко забыть вызвать initialize
, и, как правило, объект должен быть полностью допустимым после построения, существует удобный фабричный метод под названием Class # new
, который делает все, что работает за вас, и выглядит примерно так:
class Class
def new(*args, &block)
obj = allocate
obj.initialize(*args, &block)
return obj
end
end
{ {1}} [Примечание: на самом деле initialize
является закрытым, поэтому необходимо использовать отражение для обхода таких ограничений доступа: obj.send (: initialize, * args, & block )
]
Это, кстати, причина, по которой для создания объекта вы вызываете метод открытого класса Foo.new
, но вы реализуете метод частного экземпляра Foo # initialize
, что, кажется, сбивает с толку многих новичков.
Однако ничего из этого никоим образом не встроено в язык.Тот факт, что основной фабричный метод для любого класса обычно называется new
, является просто соглашением (и иногда мне хотелось бы, чтобы он был другим, потому что он похож на конструкторы в Java, но полностью ] другой). В других языках у конструктора должно быть определенное имя. В Java он должен иметь то же имя, что и класс, что означает, что а) может быть только один конструктор и б) анонимные классы не могут иметь конструкторов, потому что у них нет имен. В Python фабричный метод должен называться __ new __
, что опять же означает, что может быть только один. (И в Java, и в Python вы, конечно, можете иметь разные фабричные методы, но их вызов выглядит иначе, чем вызов по умолчанию, тогда как в Ruby (и в Smalltalk, откуда возник этот шаблон) он выглядит точно так же.)
В Ruby , может быть сколько угодно фабричных методов с любым именем, а фабричный метод может иметь много разных имен. (Для классов коллекций, например, фабричный метод часто имеет псевдоним []
, что позволяет писать List [1, 2, 3]
вместо List.new (1, 2) , 3)
, который больше похож на массив, что подчеркивает коллекционную природу списков.)
Вкратце:
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 __
.
Например, вот класс, который позволяет создавать максимум 2 экземпляра:
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!