Calmcode - method chains: bliss

Method chains are a nice abstraction.

1 2 3 4 5 6 7 8 9

This is the class that we have now.

class Clumper:
    def __init__(self, blob):
        self.blob = blob

    def keep(self, *funcs):
        data = self.blob
        for func in funcs:
            data = [d for d in data if func(d)]
        return Clumper(data)

    def head(self, n):
        return Clumper([self.blob[i] for i in range(n)])

    def tail(self, n):
        return Clumper([self.blob[-i] for i in range(1, n+1)])

    def select(self, *keys):
        return Clumper([{k: d[k] for k in keys} for d in self.blob])

    def mutate(self, **kwargs):
        data = self.blob
        for key, func in kwargs.items():
            for i in range(len(data)):
                data[i][key] = func(data[i])
        return Clumper(data)

    def sort(self, key, reverse=False):
        return Clumper(sorted(self.blob, key=key, reverse=reverse))

Let's look at the query we're able to make now.

(Clumper(poke_dict)
  .keep(lambda d: 'Grass' in d['type'],
        lambda d: d['hp'] < 60)
  .mutate(ratio=lambda d: d['attack']/d['hp'])
  .select('name', 'ratio')
  .sort(lambda d: d['ratio'], reverse=True)
  .head(15)
  .blob)

It's easy to make changes to the analysis here because the reading is from left to right top to bottom. We can change the order of lines which will change the order of the code which makes it easy to reason about the steps that are being applied to our data.

Final Comments

This is not the case in general with python for loops. It can get messy quite fast. It is exactly this what makes method chains so powerful and like-able. If you're interested in seeing another example of this "chain" of thinking you may appreciate this blogpost about evoluationary programming. We can also recommend checking out the modern pandas blogposts as well as the dplyr library if you're into R.