• Home
  • Jobs
  • Courses
  • Teachers
  • For business
  • Blog
  • ES/EN

0

24
Views
Pythonic way to construct a Factory/Builder class pattern for smoothing a series

I have a number of classes that implement smoothing techniques on a series of prices.

I am trying to figure the best way of implementing any of these smoothing classes within the __call__ function of another class, where some operation is then performed on the smoothed series:

i.e.

class NoSmoother:
    def __init__(self, prices):
        self.prices = prices
    
    def __call__(self):
        return self.prices

class MASmoother:
    def __init__(self, prices):
        self.prices = prices
    
    def __call__(self, window):
        return self.prices.rolling(window).mean().to_frame("price")


class ThatDoesSomethingWithSmoothedPrices():
    def __init__(self, prices):
        self.prices = prices

    def __call__(self, smoother=ma_smoother, window=3)
        smoothed_prices = SomeBuilderClassThatCallsTheCorrectSmootherClass()

As you can see, I would need the factory/builder class to implement NoSmoother if say smoother = None, otherwise, it would call the appropriate smoothing class. Of course, the returned object can vary from simple to complex, for example, if I smooth the prices using a Kalman Filter, the class can expect many more parameters.

Currently, my code instantiates class ThatDoesSomethingWithSmoothedPrices() with a price series, then calls by passing a **config.

Desired Output:

Ideally, I would like to be able to call any smoothing class from within the call function of

class ThatDoesSomethingWithSmoothedPrices().

Example implementation:

configs = {'smoother': MASmoother, 'window': 3}

processor = ThatDoesSomethingWithSmoothedPrices(prices)

output = processor(**config)

My attempt:

class Smoother:
    def __init__(self, prices):
        self.prices = prices
    
    def __call__(self, smoother, *args, **kwargs):
        return partial(smoother, **kwargs)
      
    def ma_smoother(self, window: int = 3):
        return self.prices.rolling(window).mean().to_frame("price")
        
    def no_smoother(self):
        return self.prices

class ThatDoesSomethingWithSmoothedPrices:
    def __init__(self, prices):
        self.prices = prices
    
    def __call__(self, smooth_method = 'no_smoother'):
        smoother = Smoother(prices)
        prices_smoothed = smoother(**configs)
        # do other things

if __name__ == '__main__':
   configs = {'smoother': 'ma_smoother', window=3}

   label = ThatDoesSomethingWithSmoothedPrices(**configs)

Any help greatly appreciated.

27 days ago ·

Santiago Trujillo

3 answers
Answer question

0

For simplicity, if you don't have a lot of state, I'd just use regular functions.

You can use functools.partial() to partially apply a function, i.e. in this case set the MA window:

from functools import partial


def no_smoother(values):
    return values


def ma_smoother(values, *, window):
    return values.rolling(window).mean().to_frame("price")


def get_prices():
    ...


def get_smoothed_prices(smoother):
    prices = get_prices()
    return smoother(prices)


get_smoothed_prices(smoother=no_smoother)
get_smoothed_prices(smoother=partial(ma_smoother, window=3))

EDIT

Based on the edit in the question:

configs = {'smoother': MASmoother, 'window': 3}
processor = ThatDoesSomethingWithSmoothedPrices(prices)
output = processor(**config)

would be expressed as something like

def construct_smoother(smoother, **kwargs):
    return partial(smoother, **kwargs)

smoother = construct_smoother(**configs)
# ...
27 days ago · Santiago Trujillo Report

0

Given your latest update you need to pass in the configs as well:

configs = {'smoother': MASmoother, 'window': 3}

processor = ThatDoesSomethingWithSmoothedPrices(configs, prices)

output = processor(**config)

So that means ThatDoesSomethingWithSmoothedPrices could be like this:

def ThatDoesSomethingWithSmoothedPrices(configs, prices):
    smoother = configs['smoother'](prices)
    return smoother
27 days ago · Santiago Trujillo Report

0

To create a factory, you can create a class method inside a class.

# declaration
class Factory:
  # class variables (static)
  id = 0
  
  @classmethod
  def new(cls):
    return object()

# usage
obj = Factory.new()

If you have a class that needs arguments in the constructor, then you can pass variable number of arguments. Basically * removes brackets around the list or dict you are passing.

# declaration
def function(var1, var2, *args, **kwargs):
  # transfer variable arguments to other function
  return SomeObejct(*args, **kwargs)

# usage
obj = function(1, 2, a, b, c, key=value)

In your case, you would do something like this:

# declaration
# you can also pass classes as arguments
def __call__(self, smoother=MASmoother, window=3, *args, **kwargs)
    smoothed_prices = smoother(*args, **kwargs)
    return smoothed_prices

# usage
smth = ThatDoesSomethingWithSmoothedPrices()
smoother1 = smth()
smoother2 = smth(NoSmoother, 2)
smoother3 = smth(NoSmoother, 2, arg1, arg2, key=value)
27 days ago · Santiago Trujillo Report
Answer question
Find remote jobs
Loading

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post job Plans Our process Sales
Legal
Terms and conditions Privacy policy
© 2022 PeakU Inc. All Rights Reserved.