5

I do have a class:

class BaseModel:

    def __init__(self):
        pass

    def read_data(self):
        df = ...
        return df

    def transform_input(self, df):
        df = ...
        return df

    def execute(self, df):
        df = ...
        return df

    def run(self):
        data = self.read_data()
        data = self.transform_input(data)
        data = self.execute(data)

How to avoid these methods call one after the other? is it possible to do sth like:

data = self.read_data().transform_input().execute()

?

Is it possible to chain somehow these methods and solve the issue of passing the argument (data) withing this methods chain?

Dariusz Krynicki
  • 2,544
  • 1
  • 22
  • 47

2 Answers2

5

These are not class methods, they are instance methods. For method chaining, it is imperative that each method returns an object that implements the next method in the chain. Since all of your methods are instance methods of BaseModel, you need to return an instance of BaseModel (or a descendant of it). That obviously means that you cannot return df (since presumably df is not an object of BaseClass). Store it in the instance and retrieve it at the end of the chain.

class BaseModel:

    def __init__(self):
        pass

    def read_data(self):
        self.df = ...
        return self

    def transform_input(self):
        self.df = ...
        return self

    def execute(self):
        self.df = ...
        return self

    def run(self):
        data = self.read_data().transform_input().execute().df

For difference between instance methods and class methods, this answer gives a nice overview.

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • thanks for this, I've been struggling to find examples. Given that this does not seem to be the most usual way to do things, I wonder if this logic is considered to be best practice, say, to use a class to represent a file (object), and then use instance methods to do a bunch of transformations. Ideas? – lowercase00 Dec 25 '19 at 20:32
1

These are instance methods, not class methods.

You seem to have two object types intermixed here. The first question you need to answer is whether this class is stateful or not. If it is stateful then your data would normally be part of the object itself in which case method chaining is very straightforward. You just return self from your methods and chain away.

class BaseModel:

    def __init__(self, data):
        self.data = data

    @classmethod
    def read_data(cls):
        data = #Some data read from somewhere
        return cls(data)

    def transform_input(self):
        #Transform your data
        return self

    def execute(self):
        #Do Something with data
        return self

my_object = BaseModel().transform_input().execute()

Personally I don't like method chaining.

If you need stateless objects then you can't chain the way you did in your example but you can do something else.

class BaseModel:

    def __init__(self):
        pass

    def read_data(self):
        data = #Some data read from somewhere
        return data

    def transform_input(self, data):
        #Transform your data
        return data

    def execute(self, data):
        #Do Something with data
        return data

my_object = BaseModel()
data = my_object.execute(my_object.transform_data(my_object.read_data()))

I also find this hard to read, but that is how you would do it statelessly

I personally find calling each method on its own line more readable

Michael Robellard
  • 2,268
  • 15
  • 25