简体   繁体   中英

Perl inheritance syntax error

I'm not a professional in Perl, but recently I had to implement inheritance with this language and got stuck.

Here is the screen shot of the sample (very simple) code and directory structure:

http://i.stack.imgur.com/nwv8t.png

The full error text is:

Can't locate Parent.pm in @INC (you may need to install the Parent module) (@INC contains: . /etc/perl /usr/local/lib/perl/5.18.2 /usr/local/share/perl/5.18.2 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.18 /usr/share/perl/5.18 /usr/local/lib/site_perl .) at lib/Child.pm line 2. BEGIN failed--compilation aborted at lib/Child.pm line 2. Compilation failed in require.

If there is some problem with the screen shot:

Directory tree:

.
├── lib
│   ├── Child.pm
│   └── Parent.pm
├── main.pl
└── MyProject.komodoproject

main.pl

#!/usr/bin/perl
use strict;
use warnings;

use lib::Child;

lib/Parent.pm

#!/usr/bin/perl
package lib::Parent;

sub new {
    my ($class, $arg_for) = @_;
    my $self = bless {}, $class;
    $self->_init($arg_for);
    return $self;
}

sub _init {
    my ($self, $arg_for) = @_;
    my %arg_for = %$arg_for;

    $self->{prop1} = $arg_for{prop1};
    $self->{prop2} = $arg_for{prop2};
}

1;

lib/Child.pm

#!/usr/bin/perl
use Parent;
package lib::Child;
@ISA = qw(Parent);

sub new {
    my ($class, $arg_for) = @_;
    my ($self) = Parent->new($arg_for);
    $self->_init($arg_for);
    return (bless ($self, $class));
}

sub _init {
    my ($self, $arg_for) = @_;
    my %arg_for = %$arg_for;

    $self->{param1} = $arg_for{param1};
    $self->{param2} = 'param2';
}

1;

Please advise. Thanks!

The major problem is that you are confusing yourself by putting the modules in the lib directory. This works partially because the current directory . appears in @ISA , so Perl will look there for the modules. But lib/Child.pm starts with

use Parent;
package lib::Child;
@ISA = qw(Parent);

which uses the wrong names for the packages. This should be

package lib::Child;
use lib::Parent;
@ISA = qw(lib::Parent);

or, better,

package lib::Child;
use strict;
use warnings;

use parent qw(lib::Parent);

Note that the package statement sets the current namespace, and it isn't unusual for use to import symbols into the current namespace. So your original use Parent was at a point where the current namespace was the default main , and then immediately change to lib::Child which has nothing imported. You should ordinarily put the package statement at the top of the code (together with use strict and use warnings in every Perl source file) but you got away with it here because you are using inheritance rather than importing symbols.

As long as your current working directory is the same as your source code directory, you can get away with putting the module files in the same place, and dropping the lib from the names. If you really want to use a separate directory for the modules then you can write

use lib '/path/to/lib';

at the start of main.pl , and the package names will be relative to this from thereon. Or you may want to make use of the environment variable PERL5LIB which adds the contents of the variable to the beginning of the path at the start of each program run.

You may find it useful to run just perl -V on the command line. That will show you the contents of @INC , which is where the perl interpreter searches for the modules that you use , and it will show you the effect of any changes to PERL5LIB as well.

Your @INC seems a little strange, as it contains . twice -- once at the beginning and once at the end. It looks like this

@INC contains:
.
/etc/perl
/usr/local/lib/perl/5.18.2
/usr/local/share/perl/5.18.2
/usr/lib/perl5
/usr/share/perl5
/usr/lib/perl/5.18
/usr/share/perl/5.18
/usr/local/lib/site_perl
.

and that usually indicates that you already have PERL5LIB set to . .

It may be better for you if you use one of the directories that you have access to that is already in the search list, such as /etc/perl or /usr/lib/perl5 . But you should check what's already in there to avoid name clashes.

I have access only to a Windows system at present, which means I was getting problems with the clash between your Parent module and the core parent pragma.

I have changed the name to Father , and I suggest you install these files on your own system all in the same directory and work from there. Then at least we will both know where you are!

It looks like you come from a Python background? There is no need for a separate _init method, as all work like that can be written in new . I have written Father so that it remembers a father_arg , and Child so that it has a child_arg as well as the father_arg inherited from the base class.

Here is main.pl . It creates one object of each type and uses the accessors to display the stored arguments from each.

#!/usr/bin/perl
use strict;
use warnings;

use feature 'say';

use Father;
use Child;

my $father = Father->new('Father1');
my $child  = Child->new('Father2', 'Child');

say $father->father_arg;
say $child->father_arg;
say $child->child_arg;

Here is Father.pm . Remember that it should be in the same directory as main.pl and the other module file. It is also best to remove any other module files that perl may find instead of the correct ones: your old Child.pm`, for instance.

The constructor new creates an anonymous hash, which is the usual base for objects but you can use any reference you like. It saves the passed parameter undef the father_arg key of the hash, and returns the blessed reference. The accessor father_arg just takes $self from the parameter list and uses it to return the father_arg element.

#!/usr/bin/perl
package Father;
use strict;
use warnings;

sub new {
    my ($class, $arg) = @_;
    my $self = { father_arg => $arg };
    bless $self, $class;
}


sub father_arg {
    my $self = shift;
    $self->{father_arg};
}

1;

Here is Child.pm . It uses use parent 'Father' to establish that it inherits from the Father module. This does something similar to use Father; our @ISA; push @ISA, 'Father'; use Father; our @ISA; push @ISA, 'Father'; but more concisely.

The constructor has two arguments this time in addition to the class name, and it needs to do its own stuff, as well as executing the constructor for Father . The pseudo-class SUPER is used for this, which will call the current class's base class without mentioning it explicitly. The father_arg value is passed to this initial constructor, which can then be modified to subclass the object by also adding the child_arg value. Then all that is necessary is to return the value of $self (already blessed by Father::new .

The father_arg accessor will be inherited from the base class, and a new child_arg accessor will allow the additional data to be recalled.

#!/usr/bin/perl
package Child;
use strict;
use warnings;

use parent 'Father';

sub new {
    my ($class, $father_arg, $child_arg) = @_;
    my $self = $class->SUPER::new($father_arg);
    $self->{child_arg} = $child_arg;
    $self;
}

sub child_arg {
    my $self = shift;
    $self->{child_arg};
}

1;

I hope this helps you.

lib should just be a library directory, and not actually part of the package names.

The following is how I would rework your files:

main.pl

#!/usr/bin/env perl
use strict;
use warnings;

use FindBin;
use lib "$FindBin::Bin/lib";

use Child;

lib/Parent.pm

package Parent;

use strict;
use warnings;

sub new {
    my ($class, $arg_for) = @_;
    my $self = bless {}, $class;
    $self->_init($arg_for);
    return $self;
}

sub _init {
    my ($self, $arg_for) = @_;
    my %arg_for = %$arg_for;

    $self->{prop1} = $arg_for{prop1};
    $self->{prop2} = $arg_for{prop2};
}

1;

lib/Child.pm

package Child;

use strict;
use warnings;

use Parent;
our @ISA = qw(Parent);

sub new {
    my ($class, $arg_for) = @_;
    my ($self) = Parent->new($arg_for);
    $self->_init($arg_for);
    return (bless ($self, $class));
}

sub _init {
    my ($self, $arg_for) = @_;
    my %arg_for = %$arg_for;

    $self->{param1} = $arg_for{param1};
    $self->{param2} = 'param2';
}

1;

Ok, thank everyone, especially @Borodin and @Miller for your effort!

Here is the fully working example (based on the 2nd solution from @Borodin):

Directory tree:

.
├── lib
│   ├── Child.pm
│   └── Parent.pm
├── main.pl
└── MyProject.komodoproject

main.pl:

#!/usr/bin/perl
use strict;
use warnings;

use lib::Child;

my $child = lib::Child->new({
    prop1 => 'prop1',
    param1 => 'param1',
    prop2 => 'prop2',
    param3 => 'param3',
    param2 => 'param2',
});

print "prop1 $child->{prop1}\n";
print "prop2 $child->{prop2}\n";
print "param1 $child->{param1}\n";
print "param2 $child->{param2}\n";
print "param3 $child->{param3}\n";

lib/Parent.pm:

#!/usr/bin/perl
package lib::Parent;

sub new {
    my ($class, $arg_for) = @_;
    my $self = bless {}, $class;
    $self->_init($arg_for);
    return $self;
}

sub _init {
    my ($self, $arg_for) = @_;
    my %arg_for = %$arg_for;

    $self->{prop1} = $arg_for{prop1};
    $self->{prop2} = $arg_for{prop2};
}

1;

lib/Child.pm:

#!/usr/bin/perl
package lib::Child;
use strict;
use warnings;

use parent qw(lib::Parent);

sub _init {
    my ($self, $arg_for) = @_;
    my %arg_for = %$arg_for;

    $self->SUPER::_init(\%arg_for);

    $self->{param1} = $arg_for{param1};
    $self->{param2} = 'param2';
    $self->{param3} = $self->test();
}

sub test {
    my $self = shift;
    my $prop1 = 'other';
    if ($self->{prop1} =~ m/\.txt$/) {
        $prop1 = 'text';
    }
    return $prop1;
}

1;

The output is:

prop1 prop1
prop2 prop2
param1 param1
param2 param2
param3 other

The only thing is that the IDE still cries on line 6 in Child.pm:

Komodo:

Can't locate lib/Parent.pm in @INC (you may need to install the lib::Parent module) (@INC contains: . /etc/perl /usr/local/lib/perl/5.18.2 /usr/local/share/perl/5.18.2 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.18 /usr/share/perl/5.18 /usr/local/lib/site_perl .) at /usr/share/perl/5.18/parent.pm line 20. BEGIN failed--compilation aborted.

Eclipse:

Can't locate lib/Parent.pm in @INC (you may need to install the lib::Parent module) (@INC contains: /etc/perl /usr/local/lib/perl/5.18.2 /usr/local/share/perl/5.18.2 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.18 /usr/share/perl/5.18 /usr/local/lib/site_perl .) in /usr/share/perl/5.18/parent.pm line 20 Child.pm /Inheritance/lib Unknown Perl Problem

However, both play the main.pl file without any problem. In CLI it runs perfect also. So it seems to be good enough (sigh).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM