【问题标题】:Calling Different Functions in Python Based on Values in a List根据列表中的值在 Python 中调用不同的函数
【发布时间】:2015-07-29 09:29:36
【问题描述】:

我有一个脚本,它将指标列表作为输入,然后从数据库中获取这些指标以执行各种操作。

我的问题是不同的客户端获得不同的指标子集,但我不想每次添加新客户端时都编写新的 IF 块。所以现在,我有一个大的 IF 块,它根据相应的指标是否在列表中来调用不同的函数。处理这个问题的最优雅或 Pythonic 方式是什么?

设置和功能定义:

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

以类似的格式想象其他三个函数,因此每个唯一指标对应一个函数。现在这是脚本中获取指标列表的代码块:

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

【问题讨论】:

  • 如果您担心以最 Pythonic 的方式做事,您可能还想使用PEP 8 样式:calc_churncalc_engagement,列表中逗号后的空格等。

标签: python list python-2.7


【解决方案1】:

也许将所需的调用封装在一个对象中可能是有意义的。

如果您的客户成为对象是有意义的,特别是如果许多客户调用同一组函数来获取指标,那么您可以创建一组 Client 子类,它们调用一组预定义的函数来获取指标。

比映射字典重一点。

''' 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()

您将从execute_metrics 获得的结果是dict 将函数名称映射到该客户端的结果。

【讨论】:

    【解决方案2】:

    您可以使用静态方法创建一个类并使用getattr 来获取正确的方法。这类似于 mgilson 的建议,但您基本上可以免费获得 dict 创建:

    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)
    

    称为 calcChurn
    称为 calcEngagement
    称为 calcPenetration
    称为 calcChurn
    称为 calcChurn
    称为 calcEngagement
    未知统计:未定义

    【讨论】:

    • 那么为什么不使用字典呢?您正在创建未知属性。
    • 因为这样你只需要实现方法,不用担心将它添加到字典中。这只是风格和偏好的问题。
    • 每一种方式都是不同的步骤。您可以轻松访问内容的 dict。无论如何,+1 很有用。
    【解决方案3】:

    通常,您会创建名称到函数的映射,并使用它来计算您想要的东西:

    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
    

    【讨论】:

    • 效果很好 - 谢谢!出于好奇,有几个问题: 1. 为什么 results[action] 为 list 中的每个项目存储一个新变量,但如果我只使用 action 它只存储一个变量,该变量会随着循环的每次迭代而不断被覆盖? 2.如果client_action_map中的每个函数接受不同数量的输入,我需要做些什么不同的事情?
    • 我不确定我是否完全理解第一个问题。第二个问题当然很棘手。您可能会使用 *args 和 **kwargs 来制作它,以便所有操作都可以接受相同的参数并且根本不使用其中一些参数,或者您可能需要将其吸收并将它们全部写出来......跨度>
    猜你喜欢
    • 1970-01-01
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 2016-08-17
    相关资源
    最近更新 更多