1

Like the question posted here, I want to create a class that inherits from another class passed as an argument.

class A():
    def __init__(self, args):
        stuff

class B():
    def __init__(self, args):
        stuff

class C():
    def __init__(self, cls, args):
        self.inherit(cls, args)

args = #arguments to create instances of A and B
class_from_A = C(A, args) #instance of C inherited from A
class_from_B = C(B, args) #instance of C inherited from B

I want to do this so that I can keep track of calls I make to different web api's. The thought is that I am just adding my own functionality to any api-type object. The problem with the solution to the linked question is that I don't want to have to go through the additional "layer" to use the api-type object. I want to say obj.get_data() instead of obj.api.get_data().

I've tried looking into how super() works but haven't came across anything that would help (although I could've easily missed something). Any help would be nice, and I'm open to any other approaches for what I'm trying to do, however, just out of curiosity I'd like to know if this is possible.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • 1
    This seems like an [XY problem](https://meta.stackexchange.com/q/66377/343832). You probably want to use composition instead, like the linked question says, though how to accomplish `obj.get_data = obj.api.get_data` is another question. So, how exactly does `C` keep track of calls to `A` and `B`? Do `A` and `B` have the same methods? – wjandrea Nov 29 '21 at 00:23
  • The `AutoEncoder` class is *not* conditionally inheriting anything in the accepted answer to the linked question. – martineau Nov 29 '21 at 00:31
  • @wjandrea You may be right... if I used composition could you point me in a direction to accomplish the `obj.get_data = obj.api.get_data` issue? (To be frank I'm not very familiar with inheritance or composition) – Hudson Hochstedler Nov 29 '21 at 00:53
  • @Hudson We'd need some info from you first, which I've already asked for: *"how exactly does C keep track of calls to A and B? Do A and B have the same methods?"* – wjandrea Nov 29 '21 at 00:55
  • @wjandrea A and B do not have the same methods. I am open to how C keeps track of calls: I mentioned in another comment that your right in that this is an XY problem. Should I edit this question or create a new post? – Hudson Hochstedler Nov 29 '21 at 01:01
  • @Hudson Oh! If you don't know how to do `C`, then work on that first. This question looks promising: [Better way to log method calls in Python?](/q/5103735/4518341) – wjandrea Nov 29 '21 at 01:06
  • @wjandrea well the `C` I'm going after is not to just count/log the calls but to add functionality to already created api-type classes so that I don't overstep any rate limits. The reason I posed this question is because I was thinking of creating a method of `C` that could be run as a separate thread that would constantly be updating a `can_call` attribute. I hope that makes sense – Hudson Hochstedler Nov 29 '21 at 01:15
  • @Hudson That makes sense, but this is getting out of my wheelhouse. Surely rate-limiting is a common problem, so try looking for existing solutions or libraries – wjandrea Nov 29 '21 at 01:46

1 Answers1

0

I don't think it's possible because __init__ is called after __new__ which is where you would specify base classes, but I think you can achieve your goal of tracking api calls using a metaclass. Since you didn't give any examples of what tracking the calls means, I'll leave you with an example metaclass which counts method calls. You can adapt it to your needs.

Another alternative would be to subclass A and B with methods that track whatever you need, and just return super().whatever(). I think I'd prefer that method unless A and B contain too many methods worth managing like that.

Here's an implementation from python-course.eu, by Bernd Klein. Click the link for more detail.

class FuncCallCounter(type):
    """ A Metaclass which decorates all the methods of the 
        subclass using call_counter as the decorator
    """
    
    @staticmethod
    def call_counter(func):
        """ Decorator for counting the number of function 
            or method calls to the function or method func
        """
        def helper(*args, **kwargs):
            helper.calls += 1
            return func(*args, **kwargs)
        helper.calls = 0
        helper.__name__= func.__name__
    
        return helper
    
    
    def __new__(cls, clsname, superclasses, attributedict):
        """ Every method gets decorated with the decorator call_counter,
            which will do the actual call counting
        """
        for attr in attributedict:
            if callable(attributedict[attr]) and not attr.startswith("__"):
                attributedict[attr] = cls.call_counter(attributedict[attr])
        
        return type.__new__(cls, clsname, superclasses, attributedict)
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Brady Dean
  • 3,378
  • 5
  • 24
  • 50