【问题标题】:Expensive operation done once in a function that is called many times, Python 3在多次调用的函数中完成一次昂贵的操作,Python 3
【发布时间】:2018-09-28 16:21:48
【问题描述】:

我在 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 包来分析这个小代码 sn-p 时,我看到了这些可怕的结果:

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 ——但这种类型的优化并不能解决我目前的问题——我如何才能在 inside def 后加载集合 group_ids堵塞?

感谢您的宝贵时间。

【问题讨论】:

  • 没有测试,不过显然你可以存储它as a function attribute
  • 这个问题解决了吗?
  • 我会说“是”
  • 嗯,在这里使用lru_cache 的明显方法是重构您的代码以拥有一个缓存def load_group_ids,然后从verify_group 调用它跨度>
  • @juanpa.arrivillaga 恕我直言,使用lru_cache 缓存没有参数的函数是用大炮射击苍蝇。

标签: python python-3.x optimization


【解决方案1】:

没有 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 时计算该设置,否则您假设调用者为您完成了工作。

【讨论】:

  • 按我的口味,封口似乎是最优雅的。我没想到!谢谢,
  • @fiacre 我是这个结构的粉丝。您可以经常使用它来为sortedgroupbykey= 参数构建函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-06
  • 1970-01-01
  • 2010-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-21
相关资源
最近更新 更多