Это не говорит о том, что константа динамична. Это говорит о том, что задание динамическое.
sepp2k
Ответы:
145
Ваша проблема в том, что каждый раз, когда вы запускаете метод, вы присваиваете константе новое значение. Это недопустимо, так как это делает константу непостоянной; даже несмотря на то, что содержимое строки одинаково (во всяком случае, на данный момент), сам фактический строковый объект отличается каждый раз при вызове метода. Например:
deffoo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Возможно, если бы вы объяснили свой вариант использования - почему вы хотите изменить значение константы в методе - мы могли бы помочь вам с лучшей реализацией.
Возможно, вы предпочитаете иметь в классе переменную экземпляра?
classMyClassclass << selfattr_accessor:my_constantenddefmy_methodself.class.my_constant = "blah"endend
p MyClass.my_constant #=> nil
MyClass.new.my_method
p MyClass.my_constant #=> "blah"
Если вы действительно хотите изменить значение константы в методе, а ваша константа является String или Array, вы можете «обмануть» и использовать #replaceметод, чтобы объект принял новое значение без фактического изменения объекта:
classMyClass
BAR = "blah"defcheat(new_bar)
BAR.replace new_bar
endend
p MyClass::BAR #=> "blah"
MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
ОП никогда не говорил, что хочет изменить значение константы, а просто хотел присвоить значение. Частый вариант использования, приводящий к этой ошибке Ruby, - это когда вы создаете значение в методе из других ресурсов времени выполнения (переменных, аргументов командной строки, ENV), обычно в конструкторе, например def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end. Это один из тех случаев, когда у Ruby нет простого пути.
Arnaud Meuret 06
2
@ArnaudMeuret В этом случае вам нужна переменная экземпляра (например @variable), а не константа. В противном случае вам придется переназначать DBкаждый раз, когда вы создаете новый экземпляр этого класса.
Ajedi32 01
2
@ Ajedi32 Эта ситуация обычно возникает из-за внешних ограничений, а не из-за выбора дизайна, такого как мой пример с Sequel. Я хочу сказать, что присвоение значения константе разрешено Ruby в определенных областях, но не в других. Раньше разработчик должен был мудро выбрать, когда выполнять задание. Руби изменилась на этом. Не всем на пользу.
Arnaud Meuret 06
2
@ArnaudMeuret Я признаю, что никогда раньше не использовал Sequel, поэтому я не могу сказать это со 100% уверенностью, но просто просматривая документацию по Sequel, я не вижу ничего, что говорило бы, что вы ДОЛЖНЫ присвоить результат Sequel.connectконстанте с именем DB . Фактически, в документации прямо говорится, что это всего лишь рекомендация. Для меня это не похоже на внешнее ограничение.
Ajedi32 06
@ Ajedi32 1) Я никогда не писал, что (имя константы или даже то, что вам нужно было где-то ее сохранить), это просто пример 2) Ограничение в том, что ваше программное обеспечение может не иметь необходимой информации, пока вы обычно не находитесь в динамическом контексте .
Arnaud Meuret 03
71
Поскольку константы в Ruby не предназначены для изменения, Ruby не рекомендует вам назначать им части кода, которые могут выполняться более одного раза, например, внутренние методы.
В нормальных условиях вы должны определить константу внутри самого класса:
Если по какой-то причине вам действительно нужно определить константу внутри метода (возможно, для какого-то типа метапрограммирования), вы можете использовать const_set:
Опять же, const_setэто не то, к чему вам действительно следует прибегать при нормальных обстоятельствах. Если вы не уверены, действительно ли хотите назначать константы таким образом, вы можете рассмотреть одну из следующих альтернатив:
Переменные класса
Переменные класса во многих отношениях ведут себя как константы. Это свойства класса, и они доступны в подклассах того класса, для которого они определены.
Разница в том, что переменные класса предназначены для модификации и, следовательно, могут быть без проблем назначены внутренним методам.
classMyClassdefself.my_class_variable
@@my_class_variable
enddefmy_method
@@my_class_variable = "foo"endendclassSubClass < MyClassend
MyClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
SubClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
MyClass.new.my_method
MyClass.my_class_variable #=> "foo"
SubClass.my_class_variable #=> "foo"
Атрибуты класса
Атрибуты класса - это своего рода «переменная экземпляра класса». Они немного похожи на переменные класса, за исключением того, что их значения не используются совместно с подклассами.
И просто для полноты я, вероятно, должен упомянуть: если вам нужно назначить значение, которое может быть определено только после того, как ваш класс был создан, есть большая вероятность, что вы действительно ищете простую старую переменную экземпляра.
В Ruby любая переменная, имя которой начинается с заглавной буквы, является константой, и вы можете назначить ей только один раз. Выберите один из этих вариантов:
Вы не можете назвать переменную заглавными буквами, иначе Ruby будет считать ее константой и захочет, чтобы ее значение оставалось постоянным, и в этом случае изменение ее значения будет ошибкой «динамической ошибкой присвоения констант». Со строчными буквами должно быть все в порядке
Ruby не нравится, что вы назначаете константу внутри метода, потому что это рискует переназначить. Несколько ответов SO передо мной дают альтернативу назначению его вне метода, но в классе, что является лучшим местом для его назначения.
Weicome для SO Джон. Вы можете подумать об улучшении этого ответа, добавив образец кода того, что вы описываете.
Cleptus
0
Большое спасибо Дориану и Фрогцу за напоминание о методе массива (и хэша) #replace, который может «заменять содержимое массива или хеша».
Представление о том, что значение CONSTANT может быть изменено, но с раздражающим предупреждением, является одним из немногих концептуальных ошибок Ruby - они должны быть либо полностью неизменяемыми, либо полностью отбрасывать идею константы. С точки зрения кодировщика, константа является декларативной и преднамеренной, сигналом для других о том, что «это значение действительно неизменяемо после объявления / назначения».
Но иногда «очевидное заявление» фактически исключает другие полезные возможности в будущем. Например...
Там являются законными прецеденты , когда значение «постоянного в» может действительно нужно изменить, например, повторно загрузку ARGV из REPL типа быстрой петли, а затем перезапустив ARGV через более (последующий) OptionParser.parse! звонки - вуаля! Предоставляет "аргументы командной строки" совершенно новую динамическую утилиту.
Практическая проблема заключается либо в предположении, что «ARGV должен быть константой», либо в собственном методе инициализации optparse, который жестко кодирует присвоение ARGV экземпляру var @default_argv для последующей обработки - этот массив (ARGV) действительно должен быть параметром, поощряющим повторный синтаксический анализ и повторное использование, где это необходимо. Правильная параметризация с соответствующим значением по умолчанию (скажем, ARGV) позволит избежать необходимости когда-либо изменять «постоянный» ARGV. Просто мысли на 2 ¢ ...
Ответы:
Ваша проблема в том, что каждый раз, когда вы запускаете метод, вы присваиваете константе новое значение. Это недопустимо, так как это делает константу непостоянной; даже несмотря на то, что содержимое строки одинаково (во всяком случае, на данный момент), сам фактический строковый объект отличается каждый раз при вызове метода. Например:
def foo p "bar".object_id end foo #=> 15779172 foo #=> 15779112
Возможно, если бы вы объяснили свой вариант использования - почему вы хотите изменить значение константы в методе - мы могли бы помочь вам с лучшей реализацией.
Возможно, вы предпочитаете иметь в классе переменную экземпляра?
class MyClass class << self attr_accessor :my_constant end def my_method self.class.my_constant = "blah" end end p MyClass.my_constant #=> nil MyClass.new.my_method p MyClass.my_constant #=> "blah"
Если вы действительно хотите изменить значение константы в методе, а ваша константа является String или Array, вы можете «обмануть» и использовать
#replace
метод, чтобы объект принял новое значение без фактического изменения объекта:class MyClass BAR = "blah" def cheat(new_bar) BAR.replace new_bar end end p MyClass::BAR #=> "blah" MyClass.new.cheat "whee" p MyClass::BAR #=> "whee"
источник
def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end
. Это один из тех случаев, когда у Ruby нет простого пути.@variable
), а не константа. В противном случае вам придется переназначатьDB
каждый раз, когда вы создаете новый экземпляр этого класса.Sequel.connect
константе с именем DB . Фактически, в документации прямо говорится, что это всего лишь рекомендация. Для меня это не похоже на внешнее ограничение.Поскольку константы в Ruby не предназначены для изменения, Ruby не рекомендует вам назначать им части кода, которые могут выполняться более одного раза, например, внутренние методы.
В нормальных условиях вы должны определить константу внутри самого класса:
class MyClass MY_CONSTANT = "foo" end MyClass::MY_CONSTANT #=> "foo"
Если по какой-то причине вам действительно нужно определить константу внутри метода (возможно, для какого-то типа метапрограммирования), вы можете использовать
const_set
:class MyClass def my_method self.class.const_set(:MY_CONSTANT, "foo") end end MyClass::MY_CONSTANT #=> NameError: uninitialized constant MyClass::MY_CONSTANT MyClass.new.my_method MyClass::MY_CONSTANT #=> "foo"
Опять же,
const_set
это не то, к чему вам действительно следует прибегать при нормальных обстоятельствах. Если вы не уверены, действительно ли хотите назначать константы таким образом, вы можете рассмотреть одну из следующих альтернатив:Переменные класса
Переменные класса во многих отношениях ведут себя как константы. Это свойства класса, и они доступны в подклассах того класса, для которого они определены.
Разница в том, что переменные класса предназначены для модификации и, следовательно, могут быть без проблем назначены внутренним методам.
class MyClass def self.my_class_variable @@my_class_variable end def my_method @@my_class_variable = "foo" end end class SubClass < MyClass end MyClass.my_class_variable #=> NameError: uninitialized class variable @@my_class_variable in MyClass SubClass.my_class_variable #=> NameError: uninitialized class variable @@my_class_variable in MyClass MyClass.new.my_method MyClass.my_class_variable #=> "foo" SubClass.my_class_variable #=> "foo"
Атрибуты класса
Атрибуты класса - это своего рода «переменная экземпляра класса». Они немного похожи на переменные класса, за исключением того, что их значения не используются совместно с подклассами.
class MyClass class << self attr_accessor :my_class_attribute end def my_method self.class.my_class_attribute = "blah" end end class SubClass < MyClass end MyClass.my_class_attribute #=> nil SubClass.my_class_attribute #=> nil MyClass.new.my_method MyClass.my_class_attribute #=> "blah" SubClass.my_class_attribute #=> nil SubClass.new.my_method SubClass.my_class_attribute #=> "blah"
Переменные экземпляра
И просто для полноты я, вероятно, должен упомянуть: если вам нужно назначить значение, которое может быть определено только после того, как ваш класс был создан, есть большая вероятность, что вы действительно ищете простую старую переменную экземпляра.
class MyClass attr_accessor :instance_variable def my_method @instance_variable = "blah" end end my_object = MyClass.new my_object.instance_variable #=> nil my_object.my_method my_object.instance_variable #=> "blah" MyClass.new.instance_variable #=> nil
источник
В Ruby любая переменная, имя которой начинается с заглавной буквы, является константой, и вы можете назначить ей только один раз. Выберите один из этих вариантов:
class MyClass MYCONSTANT = "blah" def mymethod MYCONSTANT end end class MyClass def mymethod my_constant = "blah" end end
источник
Константы в ruby не могут быть определены внутри методов. См. Примечания внизу этой страницы, например
источник
Вы не можете назвать переменную заглавными буквами, иначе Ruby будет считать ее константой и захочет, чтобы ее значение оставалось постоянным, и в этом случае изменение ее значения будет ошибкой «динамической ошибкой присвоения констант». Со строчными буквами должно быть все в порядке
class MyClass def mymethod myconstant = "blah" end end
источник
Ruby не нравится, что вы назначаете константу внутри метода, потому что это рискует переназначить. Несколько ответов SO передо мной дают альтернативу назначению его вне метода, но в классе, что является лучшим местом для его назначения.
источник
Большое спасибо Дориану и Фрогцу за напоминание о методе массива (и хэша) #replace, который может «заменять содержимое массива или хеша».
Представление о том, что значение CONSTANT может быть изменено, но с раздражающим предупреждением, является одним из немногих концептуальных ошибок Ruby - они должны быть либо полностью неизменяемыми, либо полностью отбрасывать идею константы. С точки зрения кодировщика, константа является декларативной и преднамеренной, сигналом для других о том, что «это значение действительно неизменяемо после объявления / назначения».
Но иногда «очевидное заявление» фактически исключает другие полезные возможности в будущем. Например...
Там являются законными прецеденты , когда значение «постоянного в» может действительно нужно изменить, например, повторно загрузку ARGV из REPL типа быстрой петли, а затем перезапустив ARGV через более (последующий) OptionParser.parse! звонки - вуаля! Предоставляет "аргументы командной строки" совершенно новую динамическую утилиту.
Практическая проблема заключается либо в предположении, что «ARGV должен быть константой», либо в собственном методе инициализации optparse, который жестко кодирует присвоение ARGV экземпляру var @default_argv для последующей обработки - этот массив (ARGV) действительно должен быть параметром, поощряющим повторный синтаксический анализ и повторное использование, где это необходимо. Правильная параметризация с соответствующим значением по умолчанию (скажем, ARGV) позволит избежать необходимости когда-либо изменять «постоянный» ARGV. Просто мысли на 2 ¢ ...
источник