How to check whether an attribute of an object has a value?

2.1k views Asked by At

I get an error such as "can't call method 'xxxx' on an undefined value" when attempting to check if an object has been created (by the perl module Bio::Perl).

Is there a general way of checking if an attribute has a value or not? I would have liked to do something like:

if ($the_object->the_attribute) {

But as long as the attribute is "undef", calling the method will only give me the error message. I have not been able to find a solution to this problem - which is real, because the object is created by the Bio::Perl module, and some attributes may or may not be set. Maybe I should add that I am not particularly perl-objects-savvy.

edit: Below is a relevant part of my code. The get_sequence() function is in the Bio::Perl module. On line 13, how can I make sure that there is a value (sequence in this case) before checking the length of it?

my @msgs;
my @sequence_objects;
my $maxlength = 0;

for ( @contigs ) {

    my $seq_obj;

    try {
        $seq_obj = get_sequence( 'genbank', $_ );
    }
    catch Bio::Root::Exception with {
        push @msgs, "Nothing found for $_ ";
    };

    if ( $seq_obj ) {

        my $seq_length = length( $seq_obj->seq );

        if ( $seq_length > $maxlength ) {
            $maxlength = $seq_length;
        }

        push @sequence_objects, $seq_obj;
    }
}

...
1

There are 1 answers

1
simbabque On BEST ANSWER
if ($the_object->the_attribute) {

This checks if the return value of the method the_attribute is true. True means that it's not 0, the empty string q{} or undef.

But you said you want to know whether the object exists.

Let's go over some basics first.

#   | this variable contains an object
#   |          this arrow -> tells Perl to call the method on the obj    
#   |          | this is a method that is called on $the_object
#   |          | |        
if ($the_object->the_attribute) {
#  (                          )
# the if checks the return value of the expression between those parenthesis

It looks like you're confusing a few things.

First, your $the_object is supposed to be an object. It probably came from a call like this:

my $the_object = Some::Class->new;

Or maybe it was returned from some other function call. Maybe some other object returned it.

my $the_object = $something_else->some_property_that_be_another_obj

Now the_attribute is a method (that's like a function) that returns a specific piece of data in your object. Depending on the implementation of the class (the building plan of the object), if that attribute is not set (initialized), it might either just return undef, or some other value.

But the error message you are seeing is not related to the_attribute. If it was, you'd just not call the code in the block. The if check would catch it, and decide to go to else, or do nothing if there is no else.

Your error message says you are trying to call a method on something that is undef. We know you are calling the the_attribute accessor method on $the_object. So $the_object is undef.


The easiest way to check if something has a true value is to just put it in an if. But you already seem to know that.

if ($obj) {
    # there is some non-false value in $obj
}

You've now checked that $obj is something that is true. So it could be an object. So you could now call your method.

if ($obj && $obj->the_attribute) { ... }

This will check the true-ness of $obj and only continue if there is something in $obj. If not, it will never call the right hand side of the && and you will not get an error.

But if you want to know whether $obj is an object that has a method, you can use can. Remember that attributes are just accessor methods to values stored inside the object.

if ($obj->can('the_attribute')) {
    # $obj has a method the_attribute
}

But that can blow up if $obj is not there.

If you're not sure that $obj is really an object, you can use the Safe::Isa module. It provides a method $_call_if_object1 that you can use to safely call your method on your maybe-object.

$maybe_an_object->$_call_if_object(method_name => @args);

Your call would translate to.

my $the_attribute = $obj->$_call_if_object('the_attribute');
if ($the_attribute) {
    # there is a value in the_attribute
}

The same way you can use $_isa and $_can from Safe::Isa.


1) Yes, the method starts with a $, it's really a variable. If you want to learn more about how and why this works, watch the talk You did what? by mst.