Ruby 인스턴스 변수는 언제 설정되나요?
-
05-07-2019 - |
문제
class Hello
@hello = "hello"
def display
puts @hello
end
end
h = Hello.new
h.display
위의 클래스를 만들었습니다.아무 것도 인쇄하지 않습니다.클래스 선언 중에 인스턴스 변수 @hello가 설정된 줄 알았습니다.하지만 표시 방법을 호출하면 출력은 'nil'입니다.이를 수행하는 올바른 방법은 무엇입니까?
해결책
루비의 인스턴스 변수는 루비를 처음 학습 할 때, 특히 Java와 같은 다른 OO 언어에 익숙한 경우 약간 혼란 스러울 수 있습니다.
인스턴스 변수를 간단히 선언 할 수는 없습니다.
Ruby의 인스턴스 변수에 대해 알아야 할 가장 중요한 것 중 하나는 @ Sign Prefix가있는 표기법을 제외하고는 다음과 같습니다. 그들은 처음으로 할당 될 때 생명에 빠졌습니다..
class Hello
def create_some_state
@hello = "hello"
end
end
h = Hello.new
p h.instance_variables
h.create_some_state
p h.instance_variables
# Output
[]
["@hello"]
메소드를 사용할 수 있습니다 Object#instance_variables
객체의 모든 인스턴스 변수를 나열합니다.
일반적으로 초기화 메소드에서 모든 인스턴스 변수를 "선언"하고 초기화합니다. 공개적으로 사용할 수있는 인스턴스 변수를 명확하게 문서화하는 또 다른 방법은 모듈 방법을 사용하는 것입니다. attr_accessor
(읽기/쓰기), attr_writer
(쓰기) 및 attr_reader
(읽다). 이 방법은 나열된 인스턴스 변수에 대한 다른 액세서 방법을 종합합니다.
class Hello
attr_accessor :hello
end
h = Hello.new
p h.instance_variables
h.hello = "hello"
p h.instance_variables
# Output
[]
["@hello"]
인스턴스 변수는 합성을 사용하도록 할당 될 때까지 여전히 생성되지 않습니다. Hello#hello=
방법.
KCH가 설명한 것처럼 또 다른 중요한 문제는 수업을 선언 할 때 활성화 된 다양한 상황을 알고 있어야한다는 것입니다. 수업을 선언 할 때 기본 수신기 (자체) 가장 바깥 범위에는 클래스 자체를 나타내는 객체가됩니다. 따라서 코드는 먼저 할당 할 때 클래스 인스턴스 변수를 만듭니다. @hello
수업 수준에서.
내부 방법 본인 메소드가 호출되는 객체이므로 이름으로 인스턴스 변수의 값을 인쇄하려고합니다. @hello
객체에서 존재하지 않는 객체에서 (기존 인스턴스 변수를 읽는 것이 완벽하게 합법적 임).
다른 팁
추가해야합니다 initialize
방법:
class Hello
def initialize
@hello = "hello"
end
def display
puts @hello
end
end
h = Hello.new
h.display
첫번째 @hello
코드에서는 클래스 인스턴스 변수라고 합니다.
상수가 있는 클래스 객체의 인스턴스 변수입니다. Hello
를 가리키다.(그리고 이는 클래스의 인스턴스입니다. Class
.)
기술적으로는 class
범위, 당신의 self
현재 클래스의 객체로 설정되어 있으며 @variables
당신의 현재와 관련된 self
.얘야 난 이런 것들을 설명하는 게 짜증나.
다음을 시청하면 이 모든 것을 얻을 수 있고 훨씬 더 명확해질 수 있습니다. The Pragmatic Programmers의 각 5달러짜리 스크린캐스트 모음.
(또는 여기에서 설명을 요청하시면 업데이트하도록 노력하겠습니다.)
"The Ruby Programming Language"라는 책에는 명확한 설명이 있습니다. 읽는 것은 매우 도움이 될 것입니다. 여기에 붙여 넣습니다 (7.1.16 장) :
클래스 정의 내부에서 사용되는 인스턴스 변수이지만 인스턴스 메소드 정의 외부는 클래스 인스턴스 변수.
class Point
# Initialize our class instance variables in the class definition itself
@n = 0 # How many points have been created
@totalX = 0 # The sum of all X coordinates
@totalY = 0 # The sum of all Y coordinates
def initialize(x,y) # Initialize method
@x,@y = x, y # Sets initial values for instance variables
end
def self.new(x,y) # Class method to create new Point objects
# Use the class instance variables in this class method to collect data
@n += 1 # Keep track of how many Points have been created
@totalX += x # Add these coordinates to the totals
@totalY += y
super # Invoke the real definition of new to create a Point
# More about super later in the chapter
end
# A class method to report the data we collected
def self.report
# Here we use the class instance variables in a class method
puts "Number of points created: #@n"
puts "Average X coordinate: #{@totalX.to_f/@n}"
puts "Average Y coordinate: #{@totalY.to_f/@n}"
end
end
......
클래스 인스턴스 변수는 클래스 객체의 인스턴스 변수 일 뿐이므로 attr, attr_reader 및 attr_accessor를 사용하여 액세서 메소드를 만들 수 있습니다.
class << self
attr_accessor :n, :totalX, :totalY
end
이러한 액세서가 정의 된 경우 원시 데이터를 Point.n, Point.totalx 및 Point.totaly라고 할 수 있습니다.
루비에는 "클래스 인스턴스 변수"개념이 있다는 것을 잊었습니다. 어쨌든, OP의 문제는 수수께끼처럼 보였고, KCH의 답변에 대한 힌트를 제외하고는 지금까지 어떤 답변에서도 해결되지 않았다. 그것은 범위의 문제이다. (편집에 추가 : 실제로 SRIS의 답변 하다 이 시점을 끝까지 해결하지만, 예제 코드가 문제를 이해하는 데 유용 할 수 있다고 생각하기 때문에 어쨌든이 답변을 견뎌 낼 것입니다.)
루비 클래스에서는 변수 이름이 @
하나를 참조 할 수 있습니다 둘 변수 : 인스턴스 변수 또는 a 클래스 인스턴스 변수, 수업 시간에 따라 언급 된 위치에 따라. 이것은 상당히 미묘한 Gotcha입니다.
예제는 요점을 명확히 할 것입니다. 다음은 작은 루비 테스트 클래스 (IRB에서 테스트 된 모든 코드)입니다.
class T
@@class_variable = "BBQ"
@class_instance_variable_1 = "WTF"
@class_instance_variable_2 = "LOL"
def self.class_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def initialize
@instance_variable = "omg"
# The following line does not assign a value to the class instance variable,
# but actually declares an instance variable withthe same name!
@class_instance_variable_1 = "wtf"
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def instance_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
end
나는 그들이 생각했던 것에 따라 변수를 명명했지만, 항상 그렇지는 않지만 :
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
irb> t = T.new
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf">
irb> t.instance_method
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> nil
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
그만큼 @@class_variable
그리고 @instance_variable
전자는 클래스 수준에서 정의되며 클래스 방법이나 인스턴스 방법에 언급 되든 상단에 할당 된 값을 보유합니다. 후자는 클래스 대상에서만 값을 얻습니다. T
, 클래스 메소드에서는 값이있는 알 수없는 변수를 나타냅니다. nil
.
클래스 방법은 상상력으로 명명되었습니다 class_method
값을 출력합니다 @@class_variable
그리고 둘 @class_instance_variable
예상대로, 즉 클래스의 맨 위에 초기화 된대로. 그러나 인스턴스 방법에서 initialize
그리고 instance_method
, 다른 변수 같은 이름의 액세스, 즉 클래스 인스턴스 변수가 아닌 인스턴스 변수.
당신은 그 과제를 볼 수 있습니다 initialize
메소드는 클래스 인스턴스 변수에 영향을 미치지 않았습니다 @class_instance_variable_1
, 나중에 전화하기 때문에 class_method
이전 가치를 출력하고 "WTF"
. 대신, 방법 initialize
새 인스턴스 변수를 선언했습니다. 하나입니다 또한 지명 (오해의 소지가) @class_instance_variable_1
. 할당 된 값, "wtf"
, 방법별로 출력됩니다 initialize
그리고 instance_method
.
변수 @class_instance_variable_2
예제에서 코드는 변수와 같습니다 @hello
원래 문제에서 : 클래스 인스턴스 변수로 선언되고 초기화되지만 인스턴스 메소드가 해당 이름의 변수를 지칭하면 실제로 나타납니다. 이름이 같은 인스턴스 변수입니다 - 결코 선언되지 않은 것이 있으므로 그 가치는 nil입니다.
또한 "@@"로 접두사가있는 클래스 변수를 살펴 보는 것이 좋습니다. 다음은 클래스 및 인스턴스 Vars가 어떻게 다른지 보여주는 샘플 코드입니다.
class Vars
@@classvar="foo"
def test
@instancevar="bar"
end
def Vars.show
puts "classvar: #{@@classvar}"
puts "instancevar: #{@instancevar}"
end
def instance_show
puts "classvar: #{@@classvar}"
puts "instancevar: #{@instancevar}"
end
end
# only shows classvar since we don't have an instance created
Vars::show
# create a class instance
vars = Vars.new
# instancevar still doesn't show b/c it hasn't been initialized
vars.instance_show
# initialize instancevar
vars.test
# now instancevar shows up as we expect
vars.instance_show