Store function as class variable but call without self argument

Viewed 382

I have a series of subclasses which each have their own functions for accessing data. Sometimes, it can be more complex, but by default it calls that method (see example below). The problem arises when I try to simply call the defined function and it passes self as an argument. The data access function signatures aren't defined for that and are used other ways, so it doesn't make sense to add self as an argument. How can I accomplish this design with the right implementation?

# data.py

def get_class_a_records(connection, date):
    pass


def get_class_b_records(connection, date):
    pass


class Parent:
    def get_records(self, connection, **kwargs):
        self.data_method(connection=connection, **kwargs)


class A(Parent):
    data_method = get_class_a_records


class B(Parent):
    data_method = get_class_b_records


class C(Parent):
    def get_records(self, connection, **kwargs):
        # logic for custom code/post-processing
        pass

Now if we instantiate one of these classes, we run into an issue:

a = A()
a.get_records(connection=None, date='test')
TypeError: get_class_a_records() got multiple values for argument 'connection'

This is because the call self.data_method actually passes self as an argument and we see get_class_a_records clearly doesn't have self as an argument.

1 Answers

You can call the first parameter of an instance method anything you want, but python will always insert a reference to the instance object in that position when the method is called. By assigning the get_class_a_records function to a class variable, congratulations, you've made it an instance method. More technically, python will make it an instance method when you call it.

The rules for class and static methods are different. For a class, the instance's class object is passed. And for a static method, nothing is added. That's the one you want. Just convert the function to a staticmethod and it will work.

def get_class_a_records(connection, date):
    print("class a records", connection)


def get_class_b_records(connection, date):
    pass


class Parent:
    def get_records(self, connection, **kwargs):
        self.data_method(connection=connection, **kwargs)


class A(Parent):
    data_method = staticmethod(get_class_a_records)


class B(Parent):
    data_method = staticmethod(get_class_b_records)


class C(Parent):
    def get_records(self, connection, **kwargs):
        # logic for custom code/post-processing
        pass

A().data_method("connection", "date")
Related