boot-scootin
boot-scootin

Reputation: 12515

Check if method already is in instance in object?

I'm trying to see whether an instance of an attribute already exists for my object. As you can see below, I want to do something if my Dog object has a certain attribute, via the do_something_if_has_aged method. How can I check whether a certain attribute has already been declared? Usually you would check for existence with something like this, which returns False:

obj = None
if obj:
    print(True)
else:
    print(False)

Here's my minimum reproducible example:

>>> class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def add_years(self, years):
        self.age += years
        self.has_aged = True
    def do_something_if_has_aged(self):
        if self.has_aged:
            print("The dog has aged and is %d years closer to death" % self.years)
        else:
            print("The dog hasn't aged, apparently.")


>>> dog = Dog('Spot', 3)
>>> dog.age
3
>>> dog.do_something_if_has_aged()
Traceback (most recent call last):
  File "<pyshell#193>", line 1, in <module>
    dog.do_something_if_has_aged()
  File "<pyshell#190>", line 9, in do_something_if_has_aged
    if not self.has_aged:
AttributeError: 'Dog' object has no attribute 'has_aged'
>>> dog.add_years(1)
>>> dog.age
4
>>> dog.do_something_if_has_aged()
The dog hasn't aged, apparently.

Clearly the dog has aged, though.

I apologize if the title doesn't reflect what I'm trying to convey below; I'm new to OOP.

Upvotes: 1

Views: 143

Answers (4)

Martijn Pieters
Martijn Pieters

Reputation: 1125058

Rather than test for the attribute, set a default value on the class; if an instance attribute is missing Python looks for a class attribute instead:

class Dog:
    has_aged = False  # default for all instances
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def add_years(self, years):
        self.age += years
        self.has_aged = True  # sets an instance attribute
    def do_something_if_has_aged(self):
        if self.has_aged:
            print("The dog has aged and is %d years closer to death" % self.years)
        else:
            print("The dog hasn't aged, apparently.")

(note that I had to invert your test, if self.has_aged is true you want to go into the first branch, not the other way around).

Or you can set a default value for the attribute in the __init__:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.has_aged = False
    def add_years(self, years):
        self.age += years
        self.has_aged = True
    def do_something_if_has_aged(self):
        if self.has_aged:
            print("The dog has aged and is %d years closer to death" % self.years)
        else:
            print("The dog hasn't aged, apparently.")

You can also test if an attribute is present with the hasattr() function:

def do_something_if_has_aged(self):
    if hasattr(self 'has_aged') and self.has_aged:
        print("The dog has aged and is %d years closer to death" % self.years)
    else:
        print("The dog hasn't aged, apparently.")

or by using the getattr() function with a default value:

def do_something_if_has_aged(self):
    if not getattr(self 'has_aged', False):
        print("The dog has aged and is %d years closer to death" % self.years)
    else:
        print("The dog hasn't aged, apparently.")

However, testing dynamically for attributes should not be the first option you pick; having a class default is much cleaner.

Upvotes: 3

LycuiD
LycuiD

Reputation: 2575

To check if using hasattr is perfectly fine, but in case you are looking for a quick fix for you code, you can do initialize the variable as false before hand:

class Dog:
  has_aged = False

and also the fix your condition as i think it should be reversed:

def do_something_if_has_aged(self):
  if self.has_aged:    # instead of not self.has_aged
    print("The dog has aged and is %d years closer to death" % self.years)
  else:
    print("The dog hasn't aged, apparently.")

Upvotes: 1

lungj
lungj

Reputation: 729

I would rewrite the __init__ method to include self.has_aged = False to avoid having to do inspection:

class Dog(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.has_aged = False # Starting value so it is guaranteed to be defined (unless explicitly deleted).

Now, the rest of your class should work as written. However, if you want to see if an attribute has been defined on an object, you can write this:

class Foo(object):
    def set_bar(self):
        self.bar = True # Define the attribute bar if it is not yet defined.

    def is_bar_set(self):
        return hasattr(self, 'bar')

Upvotes: 1

brianpck
brianpck

Reputation: 8254

It looks like you are looking for the hasattr built-in function:

>>> class Dog(object):
...     pass
...
>>> a = Dog()
>>> hasattr(a, 'age')
False
>>> a.age = 7
>>> hasattr(a, 'age')
True

In your case, you can modify as follows:

def do_something_if_has_aged(self):
    if hasattr(self, 'has_aged'):
        pass # do your logic

Upvotes: 4

Related Questions