V. Brunelle
V. Brunelle

Reputation: 1068

How to check if method exist from within a Python object

Context and intentions: I want to use an object m_o of type My_object as a way of interfacing with another object called s_o of type Stubborn_object. For the sake of easy understanding, they should behave like if My_object inherited from Stubborn_object, in the way that calling an attribute that doesn't exist in My_object should call the attribute in Stubborn_object.

However, the tricky thing is that I wouldn't be asking this question if I could simply inherit My_object from Stubborn_object. It appears that I can't inherit from it, and, for many reasons, I also can't modify the code of the Stubborn_object class, so I have to use it as it is. Please note that trying to inherit isn't the issue of the question here. I know that other solutions exist for my practical problem, but I really want answers to stay on topic for many reasons. I suspect that other users can have different problems than mine and still be unable to inherit a class. Furthermore, not being able to inherit a class is not the only reason that could make someone read this question. In fact, it's quite a general Python object-oriented problem. I also believe the solution of my problem could be useful in other applications, like custom error handling within the object itself when an attribute is not found, or in thread management to lock the instance as soon as an attribute is called.

In addition to the problem of inheritance, let's suppose that I can't use conditions at higher levels to handle these cases, so everything has to be done inside My_object instance or its parents. That means that I can't use hasattr(m_o, attribute_name) to determine if I should call getattr(m_o, attribute_name) or getattr(s_o, attribute_name). This also means that any try/except blocks and other preconditions must be inside the My_object class or its parents. The point of this question is not about detecting exceptions when calling an attribute from outside the My_object instance. A try/catch block normally has to be outside the My_object class, and I previously stated that this can't be allowed.

For the sake of clarity and to provide a complete verifiable example, here is a sample code of the Stubborn_object class. I know that I said I can't inherit from Stubborn_object and the following code includes an inheritable class. Providing an example of an non-inheritable object would only bring confusion and it would'nt be really helpful to the question anyway, so here is a simple example of an inheritable object. The objective of this is to make an easy to understand question, so please just consider that you can't inherit from it:

class Stubborn_object:
    def do_something(self):
        print("do_something")

    def action_to_override():
        print("action_to_override")

    def action_a(self):
        print("action_a")

    def action_b(self):
        print("action_b")

Objective: Put it simply, I want my class My_object to detect all by itself that a lacking attribute has been called and run some instructions instead of throwing an AttributeError.

Current attempts: Right now, I manually redirect method calls to the Stubborn_object instance like so (it's successful, but not reliable nor scalable because of the use of hardcoding):

class My_object():
    def __init__(self, s_o):
        self.stubborn_object = s_o

    def action_to_override(self):
        # Do stuff. This method "overrides" the Stubborn_object.action_to_override method.
        print("Here is stuff getting done instead of action_to_override")

    def action_a(self):
        return self.stubborn_object.action_a()

    def action_b(self):
        return self.stubborn_object.action_b()

s_o = Stubborn_object()
m_o = My_object(s_o)

m_o.action_to_override() # Executes Stubborn_object.do_something()
m_o.action_a()           # Executes Stubborn_object.action_a()
m_o.action_b()           # Executes Stubborn_object.action_b()

Executing this code along with the provided Stubborn_object code sample should print:

Here is stuff getting done instead of action_to_override
action_a
action_b

As you can see from methods action_a and action_b, I have to manually call the Stubborn_object methods from whithin the methods in My_object to mimic the attributes of Stubborn_object. This is ineficient, lacks of robustness and will throw an AttributeError exception if we attempt to make an action that wasn't included in the My_object code.

What if I wanted to automatically send method and attribute calls to the Stubborn_object instance without having to rewrite all of its method and attributes in My_object? I believe this can be achieved with detecting if a lacking attribute of My_object instance is called.

Expectations (or sort of): I am open to any solution that allows the My_object class or its parents to determine if the attribute is lacking or not, all within itself. So I believe I am ready to hear extremely original ideas, so go ahead.

On my part, I believe that something that uses parts of this code is the way to go, but it still lacks the "catch any called attribute" part:

class My_object():
    def __init__(self, s_o):
        # __init__ stays as it was.
        self.stubborn_object = s_o

    def action_to_override(self):
        # This method also stays as it was.
        # Do stuff. This method "overrides" the stubborn_object.action_to_override method.
        print("Here is stuff getting done instead of action_to_override")

    def run_me_when_method_is_not_found(self, method_name, **kwargs):
        print("Method " + method_name + " not in 'My_object' class.")
        return getattr(self.stubborn_object, method_name)(**kwargs)

So running those lines with the previous code sample

s_o = Stubborn_object()
m_o = My_object(s_o)

m_o.action_to_override() # Executes Stubborn_object.do_something()
m_o.action_a()           # Executes Stubborn_object.action_a()
m_o.action_b()           # Executes Stubborn_object.action_b()

will print

Here is stuff getting done instead of action_to_override
Method action_a not in 'My_object' class.
action_a
Method action_b not in 'My_object' class.
action_b

Some similar methods will have to be made for getters and setters, however, the idea stays the same. The thing is that this code lacks the ability to detect that an attribute is missing.

Question: How can I run the run_me_when_method_is_not_found when the method is not found in My_object? Especially, how can a My_object instance detect that the method doesn't exists in its class instead of throwing an AttributeError exception?

Thanks a lot.

Upvotes: 2

Views: 1234

Answers (1)

sanyassh
sanyassh

Reputation: 8550

Seems like overriding __getattribute__ will do exactly what you want: search for attribute in self.stubborn_object if it is missing in self. Put it into My_object class definition:

def __getattribute__(self, attr):
    try:
        return object.__getattribute__(self, attr)
    except AttributeError:
        return object.__getattribute__(self.stubborn_object, attr)

Upvotes: 3

Related Questions