简体   繁体   中英

Calling Different Functions in Python Based on Values in a List

I have a script that takes a list of metrics as an input, and then fetches those metrics from the database to perform various operations with them.

My problem is that different clients get different subsets of the metrics, but I don't want to write a new IF block every time we add a new client. So right now, I have a large IF block that calls different functions based on whether the corresponding metric is in the list. What is the most elegant or Pythonic way of handling this?

Setup and function definitions:

clientOne = ['churn','penetration','bounce']
clientTwo = ['engagement','bounce']

def calcChurn(clientId):
    churn = cursor.execute(sql to get churn)
    [...]
    return churn

def calcEngagement(clientId):
    engagement = cursor.execute(sql to get engagement)
    [...]
    return engagement

Imagine three other functions in a similar format, so there is one function that corresponds to each unique metric. Now here is the block of code in the script that takes the list of metrics:

def scriptName(client, clientId):
    if churn in client:
        churn = calcChurn(clientId)
    if engagement in client:
        engagement = calcEngagement(clientId)
    if penetration in client:
    [...]

Generally, you'd create a mapping of names to functions and use that to calculate the stuff you want:

client_action_map = {
  'churn': calcChurn,
  'engagement': calcEngagement,
  ...
}

def scriptName(actions, clientId):
    results = {}
    for action in actions:
        results[action] = client_action_map[action](clientId)
    return results

You can create a class with static methods and use getattr to get the correct method. It's similar to what mgilson suggests but you essentially get the dict creation for free:

class Calculators:

    @staticmethod
    def calcChurn():
        print("called calcChurn")

    @staticmethod
    def calcEngagement():
        print("called calcEngagement")

    @staticmethod
    def calcPenetration():
        print("called calcPenetration")

stats = ["churn", "engagement", "penetration", "churn", "churn", "engagement", "undefined"]

def capitalise(str):
    return str[0].upper() + str[1:]

for stat in stats:
    try:
        getattr(Calculators, "calc" + capitalise(stat))()
    except AttributeError as e:
        print("Unknown statistic: " + stat)

called calcChurn
called calcEngagement
called calcPenetration
called calcChurn
called calcChurn
called calcEngagement
Unknown statistic: undefined

Perhaps it might make sense to encapsulate the required calls inside an object.

If it makes sense for your clients to be object and especially if many clients call the same set of functions to obtain metrics, then you could create a set of Client sub classes, which call a predefined set of the functions to obtain metrics.

It's a bit heavier than the mapping dict.

''' Stand alone functions for sql commands.
    These definitions however dont really do anything.
'''


def calc_churn(clientId):
    return 'result for calc_churn'


def calc_engagement(clientId):
    return 'result for calc_engagement'


''' Client base object '''


class Client(object):
    ''' Base object allows list of functions
    to be stored in client subclasses'''

    def __init__(self, id):
        self.id = id
        self.metrics = []
        self.args = []

    def add_metrics(self, metrics, *args):
        self.metrics.extend(metrics)
        self.args = args

    def execute_metrics(self):
        return {m.__name__: m(*self.args) for m in self.metrics}


''' Specific sub classes '''


class Client1(Client):

    def __init__(self, id):
        ''' define which methods are called for this class'''

        super(Client1, self).__init__(id)
        self.add_metrics([calc_churn], id)


class Client2(Client):

    def __init__(self, id):
        ''' define which methods are called for this class'''

        super(Client2, self).__init__(id)
        self.add_metrics([calc_churn, calc_engagement], id)


''' create client objects and  '''

c1 = Client1(1)
c2 = Client2(2)

for client in [c1, c2]:
    print client.execute_metrics()

The result you will get from execute_metrics is a dict mapping the function name to its results for that client.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM