동적 상수 할당


139
class MyClass
  def mymethod
    MYCONSTANT = "blah"
  end
end

나에게 오류를 준다 :

SyntaxError : 동적 상수 할당 오류

이것이 왜 동적 상수로 간주됩니까? 그냥 문자열을 할당하고 있습니다.


34
동적 상수는 건조한 물과 같은 것입니까? :)
fl00r

39
상수가 동적이라고 말하지 않습니다. 그것은 과제가 역동적이라고 말합니다.
sepp2k

답변:


141

문제는 메소드를 실행할 때마다 상수에 새로운 값을 할당한다는 것입니다. 상수가 일정하지 않기 때문에 허용되지 않습니다. 문자열 의 내용 이 동일 하더라도 (어쨌든) 실제 문자열 객체 자체는 메소드가 호출 될 때마다 다릅니다. 예를 들면 다음과 같습니다.

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"

메소드에서 상수의 값 을 정말로 변경하고 상수가 문자열 또는 배열 인 경우, '속임수'를 사용하여 #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"

19
OP는 결코 상수 값 을 변경 하고 싶지만 값을 할당하고 싶다고 말하지 않았습니다 . 이 루비 오류로 이어지는 빈번한 유스 케이스는 일반적으로 생성자와 같은 다른 런타임 자산 (변수, 명령 행 인수, ENV)에서 메소드의 값을 빌드 할 때 def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end입니다. Ruby가 간단한 방법이없는 경우 중 하나입니다.
Arnaud Meuret

2
@ArnaudMeuret이 경우 @variable상수가 아닌 인스턴스 변수 (예 :)를 원합니다 . 그렇지 않으면 DB해당 클래스의 새 인스턴스를 인스턴스화 할 때마다 다시 할당 됩니다.
Ajedi32

2
@ Ajedi32이 상황은 일반적으로 Sequel의 예제와 같은 디자인 선택이 아닌 외부 제약 조건에서 발생합니다. 내 요점은 상수에 값을 할당하는 것은 Ruby가 특정 범위에서 허용하고 다른 범위에서는 허용하지 않는다는 것입니다. 과제를 수행 할시기를 현명하게 선택하는 것은 개발자의 몫이었습니다. 루비는 이것으로 바뀌었다. 모두를위한 것은 아닙니다.
Arnaud Meuret '12

2
@ArnaudMeuret 나는 전에 Sequel을 사용한 적이 없다는 것을 인정할 것이기 때문에 100 % 확실하게 이것을 말할 수는 없지만 Sequel에 대한 문서를 보는 것만으로도 결과를 Sequel.connect상수 DB 에 할당해야한다고 말하는 것은 아무것도 없다. . 실제로, 설명서에는 이것이 단지 권장 사항이라고 명시되어 있습니다. 그것은 나에게 외부 제약처럼 들리지 않습니다.
Ajedi32

@ Ajedi32 1) 나는 결코 그것을 쓰지 않았다 (상수의 이름이나 어딘가에 그것을 유지해야했다) 그것은 단지 예일 뿐이다 .2) 당신이 일반적으로 동적 인 맥락에있을 때까지 소프트웨어가 필요한 정보를 가지고 있지 않을 수 있다는 제약 .
Arnaud Meuret

69

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

33

Ruby에서 이름이 대문자로 시작하는 변수는 상수이며 한 번만 지정할 수 있습니다. 다음 대안 중 하나를 선택하십시오.

class MyClass
  MYCONSTANT = "blah"

  def mymethod
    MYCONSTANT
  end
end

class MyClass
  def mymethod
    my_constant = "blah"
  end
end

2
누군가가 "대문자로 시작하는 모든 변수는 상수입니다!"
ubienewbie


0

대문자로 변수 이름을 지정할 수 없거나 Ruby는 상수를 가정하여 값을 일정하게 유지하려고합니다.이 경우 값을 변경하면 "동적 상수 할당 오류"오류가 발생합니다. 소문자로 괜찮을 것

class MyClass
  def mymethod
    myconstant = "blah"
  end
end

0

루비는 재 할당의 위험이 있기 때문에 메소드 내부에 상수를 할당하는 것을 좋아하지 않습니다. 나 앞의 몇 가지 SO 답변은 메소드 외부에 할당하는 대안을 제공하지만 클래스에서는 할당하는 것이 더 좋습니다.


1
SO John에 오신 것을 환영합니다. Yo는 설명하는 샘플 코드를 추가 하여이 답변을 개선하는 것을 고려할 수 있습니다.
Cleptus

0

"배열 또는 해시의 내용을 대체 할 수있는"배열 (및 해시) 방법 #replace에 대해 상기시켜 준 Dorian과 Phrogz에게 감사드립니다.

CONSTANT의 값은 변경 될 수 있지만 성가신 경고와 함께 루비의 몇 가지 개념적인 실수 중 하나입니다. 이는 완전히 불변이거나 끊임없는 아이디어를 완전히 버려야합니다. 코더의 관점에서 볼 때 상수는 선언적이고 의도적이며 "이 값은 일단 선언 / 할당되면 실제로 변경할 수 없습니다"라는 신호를 보냅니다.

그러나 때때로 "분명한 선언"은 실제로 다른 미래의 유용한 기회를 예견합니다. 예를 들어 ...

있습니다 예를 들어, REPL 같은 메시지 루프에서 다시 로딩 ARGV는 다음 이상 (연속) OptionParser.parse 통해 ARGV를 다시 실행하십시오 "일정의"값이 정말 변경해야 할 수도 있습니다 합법적 인 사용 사례! 전화-짜잔! "명령 줄 인수"에 완전히 새로운 동적 유틸리티를 제공합니다.

실제적인 문제는 , "ARGV는 일정해야"한다는 추정 가정하에 또는 optparse의 자신의 초기화 방법에있는 하드 코드 후속 처리의 인스턴스 var에 @default_argv에 ARGV의 할당 - 정말 배열 (ARGV) 적절한 경우 재분석 및 재사용을 장려하는 매개 변수 여야합니다. 적절한 기본값 (예 : ARGV)으로 적절한 매개 변수화를 수행하면 "일정한"ARGV를 변경할 필요가 없습니다. 단지 2 ¢-생각의 생각 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.