동적 상수 할당


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.