Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Perl class - how to create instance variable instead of class variable

by richb (Scribe)
on Aug 29, 2007 at 17:03 UTC ( [id://635863]=perlquestion: print w/replies, xml ) Need Help??

richb has asked for the wisdom of the Perl Monks concerning the following question:

I need help, please, in how to make an instance variable in a class (instead of a class variable). Have searched using google and ask.com, looked through Conway's OO Perl book online...but haven't come up with any solutions. Here's some sample code that demonstrates the problem. First, the Test package.
package Test; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub getValue { my $self = shift; $self{theValue}; } sub setValue { my $self = shift; my $value = shift; $self{theValue} = $value; }
Second, the test code that references the Test package.
use Test; my $test = new Test; print "test is " .$test ."\n"; print "test getValue after new is " .$test->getValue() ."\n"; $test->setValue(1); print "test getValue after setValue(1) is ". $test->getValue() ."\n"; print "\n"; my $test1 = new Test; print "test1 is " .$test1 ."\n"; print "test1 getValue after new is " .$test1->getValue() ."\n"; #print "test getValue after test1->new is ". $test->getValue() ."\n"; $test1->setValue(2); print "test1 getValue after setValue(2) is " .$test1->getValue() ."\n" +; print "test getValue after test1->setValue(2) is ". $test->getValue() +."\n";
Here is the output with some comments added.
test is Test=HASH(0x88fbc20) Use of uninitialized value in concatenation (.) or string at test.pl l +ine 9. #expected test getValue after new is test getValue after setValue(1) is 1 test1 is Test=HASH(0x88fbde8) #different than test test1 getValue after new is 1 #wow, test1 "inherited" the value set by + the test object test1 getValue after setValue(2) is 2 test getValue after test1->setValue(2) is 2 #and now test sees 2, inst +ead of 1
I am surprised that test1 "steps on" test because the hash keys are different for test and test1. $test->setValue(1) sets the value to 1 for the test object, but after calling $test1->setValue(2), both test and test1 report 2 when getValue is called. So it seems I have a class variable here, one that holds the same value for all instances of the class. How do I get an instance variable, where each class instance maintains its own distinct value?
Many thanks for your help. Regards, Rich

Replies are listed 'Best First'.
Re: Perl class - how to create instance variable instead of class variable
by Fletch (Bishop) on Aug 29, 2007 at 17:09 UTC

    If you'd used strict and warnings you'd have gotten griped at that you're accessing the undeclared variable %self. You want to be dereferencing the hashref in the scalar $self instead (i.e. $self->{theValue}).

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Perl class - how to create instance variable instead of class variable
by lyklev (Pilgrim) on Aug 29, 2007 at 20:30 UTC
    You are almost there. What you have to keep in mind is that the
    my $self = shift;
    line in every member function gives you a blessed reference to the hash.

    So instead of writing

    $self{theValue}
    you should write
    $self -> {theValue}

    Your modified code in full:

    package Test; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub getValue { my $self = shift; $self -> {theValue}; # note '->' } sub setValue { my $self = shift; my $value = shift; $self -> {theValue} = $value; # note '->' }
Re: Perl class - how to create instance variable instead of class variable
by Anno (Deacon) on Aug 29, 2007 at 17:39 UTC
    Fletch has already pointed out the basic error. One of your comments seems to indicate another, probably related, misunderstanding. You write:

    I am surprised that test1 "steps on" test because the hash keys are different for test and test1.

    The hash keys are the same in both instances, namely theValue according to your code. What should have been different (but wasn't) are the hashes, namely the anonymous hashes references to which are your objects $test and $test1.

    Anno

Re: Perl class - how to create instance variable instead of class variable
by TGI (Parson) on Aug 30, 2007 at 01:08 UTC

    Unrelated to your question, but worth mentioning, is that it is a good idea to use explicit returns. A subroutine will implicitly return the value of the last expression evaluated, however this behavior can be unclear to maintenance programmers.

    sub getValue { my $self = shift; return $self->{theValue}; }

    You may wish to read Damian Conway's Perl Best Practices. It has a large amount of this sort of advice to offer, as well as discussion of the reasoning behind the recommendations. Some of the book's recommendations are controversial, others are widely accepted. A little searching at the monastery should provide a number of good links discussing PBP.


    TGI says moo

      One thing I still don't understand is:

      package Test; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub getValue { my $self = shift; print "\n~~~~~~~~getValue :$self \n"; $self{theValue}; } sub setValue { my $self = shift; my $value = shift; print "\n~~~~~~~~~~~set value: $self \n"; $self{theValue} = $value; } 1;
      In the output I can see that right hash is calling for test1. So Could it would be nice if you explain the bug in bit more details (difference between -> and {})

        It looks like you didn't read the advice anyone posted here, or at least didn't understand it. I'll make another effort in the hopes that you will actually read it and work at understanding.

        I've gone through the entire process of trying the advice to use the strict pragma, understanding the error message, and checking the docs for help. I've tried to follow an approach that would make sense for a novice perl programmer who does not have all the syntax and rules memorized.

        First we try running the code like this:

        package Test; use strict; use warnings; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub getValue { my $self = shift; print "\n~~~~~~~~getValue :$self \n"; $self{theValue}; } sub setValue { my $self = shift; my $value = shift; print "\n~~~~~~~~~~~set value: $self \n"; $self{theValue} = $value; } 1;

        What do we get?

        Global symbol "%self" requires explicit package name at Test.pm line 1 +5. Global symbol "%self" requires explicit package name at Test.pm line 2 +2.

        We can run splain on these errors to get a more detailed error message. The result is:

        (F) You've said "use strict" or "use strict vars", which indicates + that all variables must either be lexically scoped (using "my" or +"state"), declared beforehand using "our", or explicitly qualified to say which package the global variable is in (using "::").

        Lines 15 and 22 are where we attempt to access the values in $self with syntax like $self{theValue}. Yet for some reason, perl thinks we are trying to access %self and not $self. Why?

        Let's start by asking what each code construct means.

        • %self is a hash called 'self'. It doesn't exist, but perl says we am asking for it on line 15 and 22.
        • $self is a scalar called 'self'. In each of the subs involved in the error message, it is the first variable in the argument list. Since I am using these subs as methods the first argument is the invocant of the method. This means that self must be either the class or object the method was called on. The methods I am worried about are designed to be operate on the object. So $self is an object.

        Now we know a couple of things that we can use for researching this further.

        Let's look at what an object is in perl. Checking out perlobj, tells us pretty quickly that a perl object is a reference that knows what package it belongs to. This means that $self is a reference to some other type. Checking the new, we can see that $self is a hash reference. So at line 15 and 22 we want to be accessing values in a hash reference.

        We have established that perl thinks we are looking at specific elements in a hash. We have also established that we need to be looking at elements in a hash reference.

        Checking perldoc, we find that there are a couple articles on references. Let's read perlreftut because tutorials are often easier to understand. It says that we can access a hash ref element by doing ${$self}{theValue}. Most importantly, it says that this syntax is hard to read, and that we can use $self->{theValue} instead.

        This means that we were using the wrong syntax on lines 15 and 22, syntax intended to access a hash--not a hash reference. The strict and warnings pragmas are useful for catching this type of error. Remember that perl has different syntax for accessing array and hash members vs array ref and hash ref members.

        Please take the time to carefully read perlreftut, perlref, perlboot, perltoot, and perldsc. They provide explanations about references and how to use them, and how OO Perl works.


        TGI says moo

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://635863]
Approved by Old_Gray_Bear
Front-paged by diotalevi
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (3)
As of 2024-04-25 23:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found