Ruby classes: initialize self vs. @variable
In general, no, self.stuff = stuff
and @stuff = stuff
are different. The former makes a method call to stuff=
on the object, whereas the latter directly sets an instance variable. The former invokes a method which may be public (unless specifically declared private in the class), whereas the latter is always setting a private instance variable.
Usually, they look the same because it is common to define attr_accessor :stuff
on classes. attr_accessor
is roughly equivalent to the following:
def stuff
@stuff
end
def stuff=(s)
@stuff = s
end
So in that case, they are functionally identical. However, it is possible to define the public interface to allow for different results and side-effects, which would make those two "assignments" clearly different:
def stuff
@stuff_called += 1 # Keeps track of how often this is called, a side effect
return @stuff
end
def stuff=(s)
if s.nil? # Validation, or other side effect. This is not triggered when setting the instance variable directly
raise "Argument should not be nil"
end
@stuff = s
end
ruby should I use self. or @
When you use @lines
, you are accessing the instance variable itself. self.lines
actually goes through the lines
method of the class; likewise, self.lines = x
goes through the lines=
method. So use @
when you want to access the variable directly, and self.
when you want to access via the method.
To directly answer your question, normally you want to set the instance variables directly in your initialize
method, but it depends on your use-case.
Instance variable: self vs @
Writing @age
directly accesses the instance variable @age
. Writing self.age
tells the object to send itself the message age
, which will usually return the instance variable @age
— but could do any number of other things depending on how the age
method is implemented in a given subclass. For example, you might have a MiddleAgedSocialite class that always reports its age 10 years younger than it actually is. Or more practically, a PersistentPerson class might lazily read that data from a persistent store, cache all its persistent data in a hash.
Initializing class instance variables in Ruby
Short answer: instance variables don't get inherited by subclasses
Longer answer: the problem is that you wrote @bar = []
in the body of the class (outside any method). When you set an instance variable, it is stored on whatever is currently self
. When you're in a class body, self
is the class object Foo. So, in your example, @foo
gets defined on the class object Foo.
Later, when you try to look up an instance variable, Ruby looks in whatever is currently self
. When you call add_bar from Baz, self
is Baz. Also self
is STILL Baz in the body of add_bar (even though that method is in Foo). So, Ruby looks for @bar
in Baz and can't find it (because you defined it in Foo).
Here's an example that might make this clearer
class Foo
@bar = "I'm defined on the class object Foo. self is #{self}"
def self.get_bar
puts "In the class method. self is #{self}"
@bar
end
def get_bar
puts "In the instance method. self is #{self} (can't see @bar!)"
@bar
end
end
>> Foo.get_bar
In the class method. self is Foo
=> "I'm defined on the class object Foo. self is Foo"
>> Foo.new.get_bar
In the instance method. self is #<Foo:0x1056eaea0> (can't see @bar!)
=> nil
This is admittedly a bit confusing, and a common stumbling point for people new to Ruby, so don't feel bad. This concept finally clicked for me when I read the 'Metaprogramming' chapter in Programming Ruby (aka "The Pickaxe").
How I'd solve your problem: Look at Rails' class_attribute
method. It allows for the sort of thing you're trying to do (defining an attribute on a parent class that can get inherited (and overidden) in its subclasses).
Understanding the access of a variable assigned in initialize in Ruby
For most method calls on self, self.method_name
is equivalent to just method_name
. That's not the case for methods whose name ends with an =
, though.
The first thing to note, then, is that self.blogs = etc
doesn't call a method named blogs
and then somehow 'assign etc to it'; that line calls the method blogs=
, and passes etc
to it as an argument.
The reason you can't shorten that to just blogs = etc
, like you can with other method calls, is because blogs = etc
is indistinguishable from creating a new local variable named blogs
.
When, on the previous line, you see a bare blogs
, that is also a method call, and could just as easily have been written self.blogs
. Writing it with an implicit receiver is just shorter. Of course, blogs
is also potentially ambiguous as the use of a local variable, but in this case the parser can tell it's not, since there's no local variable named blogs
assigned previously in the method (and if there had been, a bare blogs
would have the value of that local variable, and self.blogs
would be necessary if you had meant the method call).
As for using @blogs =
instead of self.blogs =
, in this case it would have the same effect, but there is a subtle difference: if you later redefine the blogs=
method to have additional effects (say, writing a message to a log), the call to self.blogs =
will pick up those changes, whereas the bare direct access will not. In the extreme case, if you redefine blogs=
to store the value in a database rather than an instance variable, @blogs =
won't even be similar anymore (though obviously that sort of major change in infrastructure will probably have knock-on effects internal to the class regardless).
Ruby class instance variable vs. class variable
Instance variable on a class:
class Parent
@things = []
def self.things
@things
end
def things
self.class.things
end
end
class Child < Parent
@things = []
end
Parent.things << :car
Child.things << :doll
mom = Parent.new
dad = Parent.new
p Parent.things #=> [:car]
p Child.things #=> [:doll]
p mom.things #=> [:car]
p dad.things #=> [:car]
Class variable:
class Parent
@@things = []
def self.things
@@things
end
def things
@@things
end
end
class Child < Parent
end
Parent.things << :car
Child.things << :doll
p Parent.things #=> [:car,:doll]
p Child.things #=> [:car,:doll]
mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new
[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
With an instance variable on a class (not on an instance of that class) you can store something common to that class without having sub-classes automatically also get them (and vice-versa). With class variables, you have the convenience of not having to write self.class
from an instance object, and (when desirable) you also get automatic sharing throughout the class hierarchy.
Merging these together into a single example that also covers instance variables on instances:
class Parent
@@family_things = [] # Shared between class and subclasses
@shared_things = [] # Specific to this class
def self.family_things
@@family_things
end
def self.shared_things
@shared_things
end
attr_accessor :my_things
def initialize
@my_things = [] # Just for me
end
def family_things
self.class.family_things
end
def shared_things
self.class.shared_things
end
end
class Child < Parent
@shared_things = []
end
And then in action:
mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new
Parent.family_things << :house
papa.family_things << :vacuum
mama.shared_things << :car
papa.shared_things << :blender
papa.my_things << :quadcopter
joey.my_things << :bike
suzy.my_things << :doll
joey.shared_things << :puzzle
suzy.shared_things << :blocks
p Parent.family_things #=> [:house, :vacuum]
p Child.family_things #=> [:house, :vacuum]
p papa.family_things #=> [:house, :vacuum]
p mama.family_things #=> [:house, :vacuum]
p joey.family_things #=> [:house, :vacuum]
p suzy.family_things #=> [:house, :vacuum]
p Parent.shared_things #=> [:car, :blender]
p papa.shared_things #=> [:car, :blender]
p mama.shared_things #=> [:car, :blender]
p Child.shared_things #=> [:puzzle, :blocks]
p joey.shared_things #=> [:puzzle, :blocks]
p suzy.shared_things #=> [:puzzle, :blocks]
p papa.my_things #=> [:quadcopter]
p mama.my_things #=> []
p joey.my_things #=> [:bike]
p suzy.my_things #=> [:doll]
In Ruby when you initialize a class do you set an instance variable equal to a variable?
We do that so that newly-created object (not a class! with initialize and @vars, you initialize the object that was just created with new()
method!) remembers the value of number
.
Try using this one:
def initialize(number)
end
This gets a number, but does nothing with it. When this inializer ends, the object created will not remember what was the 'number'.
Here:
def initialize(number)
@foo = 5
@bar = number
end
the newly-created object will remember a 5 in @foo and the number in @bar.
The idea to name the @variable just like the parameter is just to make it easier. In the example above, it's hard to guess what the bar
is about. Instead, if I rename the @bar into @number, it wil be obvious that it holds .. the number.
def initialize(number) def initialize(number)
@bar = number <-same thing-> @number = number
end just different name end
Initializing class instance variables of subclasses from the superclass
I want all subclasses to have a variable on their singleton class / eigenclass.
Sorry, that is not what you are doing here:
puts SomeSuperClass.variable # => 'This only works for the superclass'
puts SubClass.variable # => '
Why would you think that writing
SomeSuperClass.variable
is equivalent to the pseudo code:
SomeSuperClassSingletonClass.variable
or the real code:
SomeSuperClass.singleton_class.variable
A class and it's singleton class are two different Classes.
In addition, this code:
class << self
attr_accessor :variable
@variable = ': )' # This does't seem to have any effect
end
does not create an accessor for that @variable, the same way that this code:
class Dog
attr_accessor :x
@x = 'hello'
end
puts Dog.x
...does not create an accessor for that @x variable:
--output:--
undefined method `x' for Dog:Class (NoMethodError)
What attr_accessor() does is this:
class Dog
def x
@x
end
def x=(val)
@x = val
end
#=====
@x = 'hello'
end
Those methods have nothing to do with the class instance variable @x, which was defined outside all the defs. @variables are looked up (or set) on whatever object is self at that instant. The only objects that can call those defs are instances of class Dog, and therefore x will be looked up (or set) on a Dog instance--not the Dog class.
Also note that when the line @x = 'hello'
executes, self is equal to the Dog class, therefore @x attaches itself to the Dog class.
I don't think you have a use case for setting an instance variable on a singleton class. Here is what it seems like you are trying to do:
class SomeSuperClass
class << self
attr_accessor :variable
end
self.variable = 'hello'
def self.inherited(subclass)
subclass.singleton_class.instance_eval do
attr_accessor :variable
end
subclass.variable = "Hi"
end
end
class SubClass < SomeSuperClass
end
puts SomeSuperClass.variable
puts SubClass.variable
--output:--
hello
Hi
That code creates what are known as class instance variables
. If you think you have a use case for singleton class instance variables
, let's hear it.
Related Topics
Accessing Ruby Class Variables with Class_Eval and Instance_Eval
Openssl, Rvm, Brew, Conflicting Error
Contact Form in Ruby, Sinatra, and Haml
Can't Use Compass After Installing It
Ruby Replace String with Captured Regex Pattern
Rails, Ruby, How to Sort an Array
Ruby Koans: Why Convert List of Symbols to Strings
How to Define_Method in Rails Models
"Rmagick" Gem Installation Issue
Rails: I Can't Call a Function in a Module in /Lib - What am I Doing Wrong
What Are the Ruby's Object#Taint and Object#Trust Methods
Rails 3: Generate Unique Codes (Coupons)
How to Recompile a Ruby with Rvm
Convert Datetime String to Utc in Rails
Rails Applications' Life Cycle