[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

class variable leading a double life

Amarison

7/2/2005 2:53:00 PM

Can someone please explain why the @var variable leads a double life? One
for instances and one for the class itself?



class Test
def Test.inc
@var ||= 0
@var += 1
end

def inc
@var ||= 0
@var += 1
end
end


puts Test.inc

x = Test.new
puts x.inc

y = Test.new
puts y.inc

#-------------

puts Test.inc
puts x.inc
puts y.inc


20 Answers

James Gray

7/2/2005 3:40:00 PM

0

On Jul 2, 2005, at 9:55 AM, Amarison wrote:

> Can someone please explain why the @var variable leads a double
> life? One
> for instances and one for the class itself?

@var refers to an instance variable of the current "self" object.

> class Test
> def Test.inc
> @var ||= 0
> @var += 1
> end

Here self is Test.

> def inc
> @var ||= 0
> @var += 1
> end
> end

And here, self is an instance of Test. Two different objects, two
different variables.

>
> puts Test.inc
>
> x = Test.new
> puts x.inc
>
> y = Test.new
> puts y.inc
>
> #-------------
>
> puts Test.inc
> puts x.inc
> puts y.inc

If you want it to be the same variable in both places, you need a
class variable:

class Test
@@var = 0

def Test.inc
@@var += 1
end

def inc
@@var += 1
end
end


puts Test.inc

x = Test.new
puts x.inc

y = Test.new
puts y.inc

#-------------

puts Test.inc
puts x.inc
puts y.inc

Hope that helps.

James Edward Gray II



Brent W. Hughes

7/2/2005 4:14:00 PM

0

James Edward Gray II wrote:
> On Jul 2, 2005, at 9:55 AM, Amarison wrote:
>
>> Can someone please explain why the @var variable leads a double life?
>> One
>> for instances and one for the class itself?
>
>
> @var refers to an instance variable of the current "self" object.
>
>> class Test
>> def Test.inc
>> @var ||= 0
>> @var += 1
>> end
>
>
> Here self is Test.
>
I thought that Test.inc was a class method that could be called without
ever instantiating an object of class Test, and that therefore there
would be no "self". Are you saying that Test is not just a class, but
is also an instance of some other class? I don't understand, but then
I'm just a newbie to Ruby.

>> def inc
>> @var ||= 0
>> @var += 1
>> end
>> end
>
>
> And here, self is an instance of Test. Two different objects, two
> different variables.
>
>>
>> puts Test.inc
>>
>> x = Test.new
>> puts x.inc
>>
>> y = Test.new
>> puts y.inc
>>
>> #-------------
>>
>> puts Test.inc
>> puts x.inc
>> puts y.inc
>
>
> If you want it to be the same variable in both places, you need a class
> variable:
>
> class Test
> @@var = 0
>
> def Test.inc
> @@var += 1
> end
>
> def inc
> @@var += 1
> end
> end
>
>
> puts Test.inc
>
> x = Test.new
> puts x.inc
>
> y = Test.new
> puts y.inc
>
> #-------------
>
> puts Test.inc
> puts x.inc
> puts y.inc
>
> Hope that helps.
>
> James Edward Gray II
>
>
>

Austin Ziegler

7/2/2005 4:43:00 PM

0

On 7/2/05, Brent W. Hughes <brent.hughes@comcast.net> wrote:
> I thought that Test.inc was a class method that could be called without
> ever instantiating an object of class Test, and that therefore there
> would be no "self". Are you saying that Test is not just a class, but
> is also an instance of some other class? I don't understand, but then
> I'm just a newbie to Ruby.

Ruby isn't Java or C++; in Ruby (like in Smalltalk, I think),
essentially everything is an object, including classes. Class objects
are instances of class Class.

-austin
--
Austin Ziegler * halostatue@gmail.com
* Alternate: austin@halostatue.ca


Adam P. Jenkins

7/2/2005 5:50:00 PM

0

Brent W. Hughes wrote:
> James Edward Gray II wrote:
>
>> On Jul 2, 2005, at 9:55 AM, Amarison wrote:
>>
>>> Can someone please explain why the @var variable leads a double
>>> life? One
>>> for instances and one for the class itself?
>>
>>
>>
>> @var refers to an instance variable of the current "self" object.
>>
>>> class Test
>>> def Test.inc
>>> @var ||= 0
>>> @var += 1
>>> end
>>
>>
>>
>> Here self is Test.
>>
> I thought that Test.inc was a class method that could be called without
> ever instantiating an object of class Test, and that therefore there
> would be no "self". Are you saying that Test is not just a class, but
> is also an instance of some other class? I don't understand, but then
> I'm just a newbie to Ruby.

That's exactly right. Test is an instance of class Class. There's a
description about Ruby classes and objects in the Pickaxe book at
http://www.rubycentral.com/book/cl.... The 2nd Edition, which
you have to buy, has a better description, but the free online version
has a good enough intro.

Devin Mullins

7/2/2005 8:06:00 PM

0

James Edward Gray II wrote:

> If you want it to be the same variable in both places, you need a
> class variable:
>
> class Test
> @@var = 0
>
> ...
>
> James Edward Gray II

OK, now I'm confused. I had thought that class variables were just
instance variables of the Test object, but it seems that the case is
much weirder than that. (Why?)

class Test
@fun = "hello"
@@fun = 5
def initialize
p @fun
p @@fun
@fun = "blah"
@@fun = "bloo"
end
def fun
p @fun
p @@fun
end
end
def Test.fun
p @fun
p @@fun
@fun = "bing"
@@fun = "bong"
end

irb(main):021:0> Test.fun
"hello"
NameError: uninitialized class variable @@fun in Object
from (irb):17:in `fun'
from (irb):21

irb(main):022:0> t = Test.new
nil
5
=> #<Test:0x2aba608 @fun="blah">

irb(main):023:0> Test.fun
"hello"
NameError: uninitialized class variable @@fun in Object
from (irb):17:in `fun'
from (irb):23

irb(main):024:0> t.fun
"blah"
"bloo"
=> nil

1. Class variables are these odd things that are shared among every
instance of Test.
2. Class variables of Test are not available to class methods of Test
itself.
3. The singleton class for Test can have instance variables, but not
class variables.*
4. Why didn't the second Test.fun call print "bing"?
5. Is there some way to share data between between a class's instance
methods and its class methods?

I'm going to go study the PickAxe2 now, but I figured maybe I could get
a quicker answer from some of you. :)

Devin
*I'm referring to the anonymous subclass of Class that is the class of
the Test constant itself. She sells sea shells by the sea shore.



James Gray

7/2/2005 9:19:00 PM

0

On Jul 2, 2005, at 3:05 PM, Devin Mullins wrote:

> OK, now I'm confused. I had thought that class variables were just
> instance variables of the Test object, but it seems that the case
> is much weirder than that. (Why?)

We already have a syntax for instance variables, so that's what you
use if you want an instance variable on test. The reason class
variables don't work like that is that they would then by tricky to
reach fro instance objects of the class and that wouldn't be good at
all.

Hope that makes sense.

James Edward Gray II


Eustaquio Rangel de Oliveira Jr.

7/2/2005 9:46:00 PM

0

Hi.

> I thought that Test.inc was a class method that could be called without
> ever instantiating an object of class Test, and that therefore there
> would be no "self". Are you saying that Test is not just a class, but
> is also an instance of some other class?

There is an "internal", if makes it easier to understand, representation
of Test, so @@var is there. Kind of when creating t = Test.new, t points
to @@var on the "internal" class.

This is the way you can add or remove methods to a class already
defined, you change the representation on the "internal" class who
shares your change with all other objects of the same class, and
thinking this way, share that variable also.

Kind of when you write

class Test
def method_one
puts "one!"
end
end
t1 = Test.new

and then, later

class Test
def method_two
puts "two"
end
end
t2 = Test.new

you have BOTH methods on t1 and t2, not matter that t1 was defined
before it was added, you changed that "internal" class where t1 and t2
are using as reference. When you create a class variable, think it is
stored there.

Best regards,


----------------------------
Eustáquio "TaQ" Rangel
eustaquiorangel@yahoo.com
http://b...
Usuário GNU/Linux no. 224050


Robert Klemme

7/3/2005 12:49:00 PM

0

James Edward Gray II <james@grayproductions.net> wrote:
> On Jul 2, 2005, at 3:05 PM, Devin Mullins wrote:
>
>> OK, now I'm confused. I had thought that class variables were just
>> instance variables of the Test object, but it seems that the case
>> is much weirder than that. (Why?)
>
> We already have a syntax for instance variables, so that's what you
> use if you want an instance variable on test. The reason class
> variables don't work like that is that they would then by tricky to
> reach fro instance objects of the class and that wouldn't be good at
> all.

Let me add to that that usually you want to associate a variable with the
class instance or with instances. Class variables (the ones with @@) have
some strange properties that sometimes lead to surprising behavior. I
generally recommend to not use them. Instance variables in classes are much
simpler to handle and much clearer and cleaner IMHO.

Just a simple example where each new instance gets assigned a serial number:

class Foo
@cnt = 0
def self.tick() @cnt += 1 end

attr_reader :serial

def initialize
@serial = self.class.tick
end
end

?> instances = (1..10).map { Foo.new }
=> [#<Foo:0x1017e248 @serial=1>, #<Foo:0x1017e1a0 @serial=2>,
#<Foo:0x1017e0e0 @serial=3>, #<Foo:0x1017dff0 @serial=4>, #<Foo:0x1017
df60 @serial=5>, #<Foo:0x1017deb8 @serial=6>, #<Foo:0x1017de58 @serial=7>,
#<Foo:0x1017ddc8 @serial=8>, #<Foo:0x1017dd50 @serial=9>,
#<Foo:0x1017dca8 @serial=10>]
>> instances.map {|f| f.serial}
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Note that this has to be changed slightly if you want to inherit from Foo.
I just didn't want to make this overly complicated.

Kind regards

robert

Eustaquio Rangel de Oliveira Jr.

7/3/2005 3:16:00 PM

0

Hey Robert!

> Let me add to that that usually you want to associate a variable with
> the class instance or with instances. Class variables (the ones with
> @@) have some strange properties that sometimes lead to surprising
> behavior. I generally recommend to not use them. Instance variables in
> classes are much simpler to handle and much clearer and cleaner IMHO.

Just to make things more clear,

irb(main):001:0> class Test
irb(main):002:1> @@v1 = 0 # class variable?
irb(main):003:1> @v2 = 1 # ???
irb(main):004:1> def initialize
irb(main):005:2> @v3 = 2 # instance variable?
irb(main):006:2> end
irb(main):007:1> def one
irb(main):008:2> @@v1
irb(main):009:2> end
irb(main):010:1> def two
irb(main):011:2> @v2
irb(main):012:2> end
irb(main):013:1> def three
irb(main):014:2> @v3
irb(main):015:2> end
irb(main):016:1> end
=> nil
irb(main):017:0> t = Test.new
=> #<Test:0xb7dc6574 @v3=2>
irb(main):018:0> t.one
=> 0
irb(main):019:0> t.two
=> nil
irb(main):020:0> t.three
=> 2

What is the correct names of v1, v2 and v3? v1 is a class variable, but
what about v2 and v3? We can say that v3 belongs to the class instance
(we saw that after creating the class v3 is on the output
<Test:0xb7dc6574 @v3=2>)on t, but v2 belongs to the class Test?

The answer is the difference to create the variables just after the
class statement and before the initialize method OR create it in the
initialize method, right?

And v2 is not reached by the two method, but if I change the class and
insert

irb(main):026:0> class Test
irb(main):027:1> def self.four
irb(main):028:2> @v2
irb(main):029:2> end
irb(main):030:1> end

and use

irb(main):032:0> t.class.four
=> 1

I can reach it. So where v1 and v2 exactly are named and belongs to? I
can see that each t haves it's own v3, but still didn't get the way the
or ones are. How v1 and v2 are located on different places.
Kind of

+-------+ +-----+
| Test +-----+ t |
+-------+ +-----+
| @@v1? | | @v3 |
| @v2? | +-----+
+-------+

Maybe v1 and v2 are on the same place (the Test instance - v1 needs to
be shared by all Test's, right?) but just can be reached on different
ways (v2 using four)?

On the pickaxe we have an example

class SongList
MaxTime = 5*60 # 5 minutes
def SongList.isTooLong(aSong)
return aSong.duration > MaxTime
end
end

On this case, MaxTime resides on the same place that v2 or not? Because
if I change SongList and add

class SongList
def isTooLong(aSong)
return aSong.duration > MaxTime
end
end

and use

s = SongList.new
s.isTooLong(<song here>)

I have a valid result, but something like

irb(main):057:0> class SongList2
irb(main):058:1> @maxtime = 5*60
irb(main):059:1> def SongList2.isTooLong(t)
irb(main):060:2> return t > @maxtime
irb(main):061:2> end
irb(main):062:1> def isTooLong(t)
irb(main):063:2> return t > @maxtime
irb(main):064:2> end
irb(main):065:1> end
=> nil
irb(main):066:0> s2 = SongList2.new
=> #<SongList2:0xb7d733d8>
irb(main):067:0> s2.isTooLong(301)
ArgumentError: comparison of Fixnum with nil failed
from (irb):63:in `>'
from (irb):63:in `isTooLong'
from (irb):67
from (null):0
irb(main):068:0> SongList2.isTooLong(301)
=> true
irb(main):069:0> SongList2.isTooLong(30)
=> false

gives me that error, so there is also a scope difference on a constant
like MaxTime and a variable like @maxtime, defined on the class body?

Thanks! :-)

----------------------------
Eustáquio "TaQ" Rangel
eustaquiorangel@yahoo.com
http://b...
Usuário GNU/Linux no. 224050


Robert Klemme

7/3/2005 3:46:00 PM

0

"Eustáquio Rangel de Oliveira Jr." <eustaquiorangel@yahoo.com> wrote:
> Hey Robert!
>
>> Let me add to that that usually you want to associate a variable with
>> the class instance or with instances. Class variables (the ones with
>> @@) have some strange properties that sometimes lead to surprising
>> behavior. I generally recommend to not use them. Instance
>> variables in classes are much simpler to handle and much clearer and
>> cleaner IMHO.
>
> Just to make things more clear,

I'm not sure about "more clear" with your example... :-)

> irb(main):001:0> class Test
> irb(main):002:1> @@v1 = 0 # class variable?
> irb(main):003:1> @v2 = 1 # ???
> irb(main):004:1> def initialize
> irb(main):005:2> @v3 = 2 # instance variable?
> irb(main):006:2> end
> irb(main):007:1> def one
> irb(main):008:2> @@v1
> irb(main):009:2> end
> irb(main):010:1> def two
> irb(main):011:2> @v2
> irb(main):012:2> end
> irb(main):013:1> def three
> irb(main):014:2> @v3
> irb(main):015:2> end
> irb(main):016:1> end
> => nil
> irb(main):017:0> t = Test.new
> => #<Test:0xb7dc6574 @v3=2>
> irb(main):018:0> t.one
> => 0
> irb(main):019:0> t.two
> => nil
> irb(main):020:0> t.three
> => 2
>
> What is the correct names of v1, v2 and v3? v1 is a class variable,
> but what about v2 and v3? We can say that v3 belongs to the class
> instance (we saw that after creating the class v3 is on the output
> <Test:0xb7dc6574 @v3=2>)on t, but v2 belongs to the class Test?

In fact you have v2, v2 and v3 which shows a bit unfortunate naming because
these are three different things. They are all instance variables only that
the first v2 is an instance variable of the class object while the second v2
is an instance variable of each Test instance. These are not the same nor
do they interfere with each other.

> The answer is the difference to create the variables just after the
> class statement and before the initialize method OR create it in the
> initialize method, right?

Yes. And you can create them even later. Instance variables can spring
into existing any time (i.e. during any method execution, see also
#instance_variable_get, ..set etc). Ruby is different than other OO
languages where you usually have to declare variables. In Ruby, you just
use them. Whenever you do @something=x then the instance that is currently
referred to by self will have an instance variable named "something".

> And v2 is not reached by the two method, but if I change the class and
> insert
>
> irb(main):026:0> class Test
> irb(main):027:1> def self.four
> irb(main):028:2> @v2
> irb(main):029:2> end
> irb(main):030:1> end
>
> and use
>
> irb(main):032:0> t.class.four
> => 1
>
> I can reach it. So where v1 and v2 exactly are named and belongs to? I
> can see that each t haves it's own v3, but still didn't get the way
> the or ones are. How v1 and v2 are located on different places.
> Kind of
>
> +-------+ +-----+
>> Test +-----+ t |
> +-------+ +-----+
>> @@v1? | | @v3 |
>> @v2? | +-----+
> +-------+

You're missing @v2 in the right object.

> Maybe v1 and v2 are on the same place (the Test instance - v1 needs to
> be shared by all Test's, right?) but just can be reached on different
> ways (v2 using four)?

Yes.

> On the pickaxe we have an example
>
> class SongList
> MaxTime = 5*60 # 5 minutes
> def SongList.isTooLong(aSong)
> return aSong.duration > MaxTime
> end
> end
>
> On this case, MaxTime resides on the same place that v2 or not?
> Because if I change SongList and add
>
> class SongList
> def isTooLong(aSong)
> return aSong.duration > MaxTime
> end
> end
>
> and use
>
> s = SongList.new
> s.isTooLong(<song here>)
>
> I have a valid result, but something like
>
> irb(main):057:0> class SongList2
> irb(main):058:1> @maxtime = 5*60
> irb(main):059:1> def SongList2.isTooLong(t)
> irb(main):060:2> return t > @maxtime
> irb(main):061:2> end
> irb(main):062:1> def isTooLong(t)
> irb(main):063:2> return t > @maxtime
> irb(main):064:2> end
> irb(main):065:1> end
> => nil
> irb(main):066:0> s2 = SongList2.new
> => #<SongList2:0xb7d733d8>
> irb(main):067:0> s2.isTooLong(301)
> ArgumentError: comparison of Fixnum with nil failed
> from (irb):63:in `>'
> from (irb):63:in `isTooLong'
> from (irb):67
> from (null):0
> irb(main):068:0> SongList2.isTooLong(301)
> => true
> irb(main):069:0> SongList2.isTooLong(30)
> => false
>
> gives me that error, so there is also a scope difference on a constant
> like MaxTime and a variable like @maxtime, defined on the class body?

The trick is in the lookup: @something always refers to the current instance
at the moment of execution. Your instance method isTooLong (which btw. in
Ruby convention would be named too_long?) refers to a @maxtime instance
variable of the SongList2 instance - as these instances don't have a
@maxtime you get nil.

Constants are looked up via in class scope - naturally because you cannot
have constances in an instance.

Kind regards

robert