class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
дає мені помилку:
SyntaxError: похибка динамічного постійного призначення
Чому це вважається динамічною постійною? Я просто присвоюю йому рядок.
class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
дає мені помилку:
SyntaxError: похибка динамічного постійного призначення
Чому це вважається динамічною постійною? Я просто присвоюю йому рядок.
Відповіді:
Ваша проблема полягає в тому, що кожного разу, коли ви запускаєте метод, ви присвоюєте нове значення постійному. Це не дозволено, оскільки це робить постійним непостійним; незважаючи на те, що вміст рядка однаковий (на даний момент, так чи інакше), сам фактичний об'єкт рядка відрізняється щоразу, коли метод викликається. Наприклад:
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 або масив, ви можете "обдурити" і використовувати #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
. Це один із тих випадків, коли у Рубі немає простого способу.
@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 буде вважати її постійною і хотітиме, щоб вона зберігала значення постійним, і в цьому випадку зміна її значення буде помилкою "помилка динамічної постійної присвоєння". З нижнього регістру має бути добре
class MyClass
def mymethod
myconstant = "blah"
end
end
Рубі не подобається, що ви призначаєте константу всередині методу, оскільки це загрожує повторним призначенням. Перед нами кілька відповідей ТА дають альтернативу призначити його поза методом - але в класі, який є кращим місцем для його призначення.
Велике спасибі Доріану та Фрогз за те, що вони нагадали мені про метод масиву (і хеш) #replace, який може "замінити вміст масиву чи хеша".
Думка про те, що значення CONSTANT можна змінити, але з дратівливим попередженням, є одним з небагатьох концептуальних помилок Рубі - вони повинні бути повністю незмінними, або взагалі скидати постійну ідею. З точки зору кодера, константа є декларативним та навмисним, сигналом для інших, що "це значення справді незмінне після оголошення / присвоєння".
Але іноді "очевидна заява" фактично виключає інші, майбутні корисні можливості. Наприклад...
Там є законними прецеденти , коли значення «постійного в» може дійсно потрібно змінити, наприклад, повторно завантаження ARGV з REPL типу швидкої петлі, а потім перезапустивши ARGV через більш (наступний) OptionParser.parse! дзвінки - вуаля! Дає "аргументам командного рядка" абсолютно нову динамічну утиліту.
Практична проблема полягає або в припущенному припущенні, що "ARGV повинен бути константою", або у власному методі ініціалізації optparse, який жорстко кодує призначення ARGV екземпляру var @default_argv для подальшої обробки - цей масив (ARGV) дійсно повинен бути параметром, заохочуючи повторний аналіз та повторне використання, де це доречно. Належна параметризація з відповідним за замовчуванням (скажімо, ARGV) дозволить уникнути необхідності колись змінювати "постійний" ARGV. Просто кілька ¢-варті думок ...