在多次调用的函数Python 3中一次执行的昂贵操作

时间:2018-09-28 16:21:49

标签: python python-3.x optimization

我在json中有很长的组列表,我想要一个小实用程序:

def verify_group(group_id):
    group_ids = set()
    for grp in groups:
        group_ids.add(grp.get("pk"))
    return group_id in group_ids

显而易见方法是将集合加载到函数外部,或者声明全局变量-但假设我不需要全局变量。

在静态类型语言中,我可以说该集合是静态的,我相信这将实现我的目标。在python中如何做类似的事情?也就是说:第一个调用初始化集合 group_ids ,随后的调用使用在第一个调用中初始化的集合。

顺便说一句,当我使用profilestats程序包分析此小代码段时,我看到了以下令人恐惧的结果:

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      833    0.613    0.001    1.059    0.001 verify_users_groups.py:25(verify_group)
  2558976    0.253    0.000    0.253    0.000 {method 'get' of 'dict' objects}
  2558976    0.193    0.000    0.193    0.000 {method 'add' of 'set' objects}

我尝试添加functools.lru_cache-但这种类型的优化无法解决我当前的问题-我如何在内部 def中加载集合 group_ids 块?

谢谢您的时间。

1 个答案:

答案 0 :(得分:3)

没有static的等效项,但是您可以通过不同的方式实现相同的效果:

一种方法是滥用臭名昭著的可变默认参数:

def verify_group(group_id, group_ids=set()):
    if not group_ids:
        group_ids.update(grp.get("pk") for grp in groups)
    return group_id in group_ids

但是,这允许调用者更改该值(这可能是您的功能或错误)。

我通常更喜欢使用闭包:

def make_group_verifier():
    group_ids = {grp.get("pk") for grp in groups}
    def verify_group(group_id):
        # nonlocal group_ids # if you need to change its value
        return group_id in group_ids
    return verify_group

verify_group = make_group_verifier()

Python是一种OOP语言。您描述的是一个实例方法。用集合初始化类,然后在实例上调用方法。

class GroupVerifier:
    def __init__(self):
        self.group_ids = {grp.get("pk") for grp in groups}
    def verify(self, group_id):
        # could be __call__
        return group_id in self.group_ids

我还要补充一点,它取决于API设计。如果他们需要性能,您可以让他们负责预先计算并提供价值。例如,random.choices就是这种选择。 cum_weights参数不是必需的,但它允许用户消除性能关键代码中每次调用时计算该阵列的成本。因此,不要使用可变的参数,而是使用None作为默认值,并仅在传递的值为None时计算该设置,否则您将假定调用者为您完成了工作。