Marcelo Gazzola
Marcelo Gazzola

Reputation: 1083

Dynamically call functions in Python

I am trying to test a Python library called Tulip dynamically. To do it I need to call the proper ti.<indicator_name> and pass the arguments to call the method.

The problem is, each method has fixed number of parameters and I don't know how to pass it properly.

Let's say I want to test ti.sma method that requires two arguments real and period

def sma(real, period):
    """
    Simple Moving Average
    """

    return lib.sma([real], [period])

So, I would need to call it as:

sma_test = ti.sma(real=[25,20,22,30,22,28,25,30,21,23,24,22], period=5)

So my question is, how do I call the method above dynamically using the json below as payload?

{
    "short_name": "sma",
    "data": [74.333511, 73.61084, 74.197395, 73.848442, 75.036385, 76.630219, 76.803459, 77.385063],
    "period": 5
}

I have made this validator where I could get the function object but how about the parameters?

import pandas as pd
import numpy as np
import tulipy as ti
import datetime
import uuid
import inspect

def validate_indicator(payload):
    data = np.array(payload.get('data'))
    try:
        indicator = getattr(ti, payload.get('short_name'))
        validation_test = indicator(data)

If I run the code above I get the Exception TypeError obviously because I didn't pass the required argument period in output = indicator(data). I believe the way to get there is to make a new function with optional *args

validate_indicator(
{
    "short_name": "sma",
    "data": [74.333511, 73.61084, 74.197395, 73.848442, 75.036385, 76.630219, 76.803459, 77.385063],
    "period": 5
}
)

Result:
"sma() missing 1 required positional argument: 'period'"

Another example, if I want to test ti.bbands that requires real, period and stddev as arguments.

def bbands(real, period, stddev):
    """
    Bollinger Bands
    """

    return lib.bbands([real], [period, stddev])

Upvotes: 0

Views: 110

Answers (1)

Miguel Alorda
Miguel Alorda

Reputation: 692

You actually can use **kwargs:

File test.py

import test2

data = {
    "short_name": "sma",
    "data": [74.333511, 73.61084, 74.197395, 73.848442, 75.036385, 76.630219, 76.803459, 77.385063],
    "period": 5
}

f = getattr(test2, data.pop('short_name'))
f(**data)

File test2.py:

def sma(data, period):
    print(data)
    print(period)
> python3 test.py
[74.333511, 73.61084, 74.197395, 73.848442, 75.036385, 76.630219, 76.803459, 77.385063]
5

Note: If you want to use *args, you could call the function as:

f(*[value for value in data.values()])

Edit

This would be a function that accepts the data dict as a parameter and calls the corresponding function for you:

def validate_function(data):
    f = getattr(ti, data.pop('short_name'))
    f(**data)

Upvotes: 1

Related Questions