python-3.xclassexceptionexpander

can python3 create missing methods on the fly through exception handling?


I want to modify class instance behavior.

class A:
    def __init__(self): pass
    def methodA(self): return "methodA"

a = A()
print(a.methodA())
print(a.methodB())

The exception thrown during a.methodB() is in the context of class A instance a. The exception handler could add a methodB to instance a with a default behavior for new methods.

I have been digging for hints on how to halt the exception in the context of a, add the method, call the method, and abort the exception. I expect many to say "why?". I need this to create chained instances on the fly to be addressed by dotnames such as for class A instance a, I want to reference a.b.c.d.e.f, or call a.b.c.d.e.f() and have the chain become instanced with default structure at every level... instanced by reference. Somehow, the interpreter must override the NotImplementedError or something like that.

This feels beyond my ability to implement without a little help. It does not seem impossible. Is it?


Solution

  • doing some research on this and found a method: getattr

    good blog: https://medium.com/@satishgoda/python-attribute-access-using-getattr-and-getattribute-6401f7425ce6

    example:

    class ABC:
        def __init__(self):
            pass
    
        def methodA(self):
            return "methodA"
    
        def __getattr__(self, name):
            # Define the default behavior for undefined methods
            def default_method(*args, **kwargs):
                return f"default behavior for {name}"
    
            # Return the default method when an undefined method is accessed
            return default_method
    
    
    a = ABC()
    print(a.methodA())  # Output: "methodA"
    print(a.methodB())  # Output: "default behavior for methodB"
    

    Here are my mods which delivered exactly what I had hoped:

    class Foo:
        def __init__(self,name):
            self.name = name
        def __str__(self):
            return self.name
        def __getattr__(self, name):
            self.__dict__[name] = Foo(self.name+'.'+name)
            return self.__dict__[name]
    
    foo = Foo("foo")
    print(foo.bar.baz)