Почему "instance.send(:initialize, *args, **kwargs, &block)" терпит неудачу только изнутри Class#new?
Я застрял на этом довольно давно. Взгляните на это:
class SuperClass
def self.new(*args, **kwargs, &block)
i = allocate()
# Extra instance setup code here
i.send(:initialize, *args, **kwargs, &block)
return i
end
end
class Test < SuperClass
def initialize
puts "No args here"
end
end
Класс SuperClass
в основном "переопределение" по умолчанию new
метод, так что некоторая дополнительная инициализация может произойти до initialize
,
Теперь следующее работает просто отлично:
t = Test.allocate
t.send(:initialize, *[], **{}, &nil)
Однако это не так:
t = Test.new
ArgumentError: неверное количество аргументов (1 для 0) from (pry):7: в `initialize'
Это терпит неудачу на этой линии в SuperClass
:
i.send(:initialize, *args, **kwargs, &block)
Но, по-видимому, это только сбой, если вызывается new
метод. Я подтвердил, что args == []
, kwargs == {}
а также block == nil
,
Кто-нибудь может объяснить это?
Рубиновая версия:
ruby 2.2.3p173 (2015-08-18, редакция 51636) [x86_64-linux]
Пожалуйста, воздержитесь от предложения, чтобы я не перегружал Class.new
, Я знаю, что могу использовать Class.inherited
а также Class.append
за тот же результат. Этот вопрос только о том, почему звонок initialize
выходит из строя.
2 ответа
Давайте рассмотрим более простой пример, особенно потому, что проблема не столь специфична, как вопрос, и его название заставляет его выглядеть, но убедитесь сами.
def m # takes no arguments
end
m(**{}) # no argument is passed
h = {}
m(**h) # an argument is passed => ArgumentError is raised
Это несоответствие было введено в 2.2.1 коммитом, предназначенным для исправления ошибки сегментации, включающей **{}
( Ошибка № 10719). Совершать особые случаи **{}
не передавать аргумент. Другие способы, такие как **Hash.new
а также h={};**h
все еще передать пустой хэш в качестве аргумента.
Предыдущие версии последовательно поднимают ArgumentError
( демо). Я могу ошибаться, но я верю, что это предполагаемое поведение. Однако это может или не может быть тем, кто на самом деле хочет. Так что если вы думаете, что двойное разделение пустого хэша не должно передавать аргумент (например, **{}
на данный момент) и, следовательно, работают подобно разбрызгиванию пустого массива, об этом есть открытая проблема ( ошибка №10856). Также упоминается это относительно новое несоответствие.
Просто *args
будет захватывать все аргументы, включая ключевые аргументы, в случае, если вам не нужно ссылаться kwargs
отдельно в new
метод:
class SuperClass
def self.new(*args, &block)
i = allocate
# Extra instance setup code here
i.send(:initialize, *args, &block)
i
end
end