【问题标题】:Python collections.Counter, how to avoid double lookupPython collections.Counter,如何避免双重查找
【发布时间】:2020-02-15 20:23:33
【问题描述】:

在这里尝试学习python:

假设我有一个非常大的 Counter 对象,如果该值大于 0,我想在其中更改特定键的值。所以是这样的:

from collections import Counter
...
A = Counter(huge_list)
...
if (A[some_key] > 0):
    ...
    A[some_key] -= 1
    ...
...

我的问题是,如何避免重复查找相同的值 A[some_key] ?例如,在 C++ 中发生的方式是返回对 A[some_key] 的引用,然后更改该引用指向的值,所以可能是这样的:

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main()
{
  map<string, int> A = { {"key", 1}, {"key2", 2}};

  int &a = A["key"];
  if ( a > 0 ) {
       a--;
  }
  cout << A["key"] << endl;
}

它会很高兴地打印 0。

我认为这里的问题是 Counter 的值是不可变的对象;正在做:

a = A[some_key]

"a" 将是对对象 A[some_key] 的引用。但随后:

a-=1

将简单地将“a”分配给另一个具有相同值的 int 对象,并且 A[some_key] 将保持不变。

我想一种解决方案是使用默认字典,其值是单个整数的列表;列表是可变的,因此我可以删除整数值并插入一个减 1 的新值,此时 A[some_key] 会改变。但是有没有更好的方法?

这可能也意味着我在这里描述的问题应该对于每个不可变对象的可迭代集合都是常见的?

你有什么想法?

【问题讨论】:

  • 它是字典,它使用哈希来获取项目,所以它工作得很快。您应该对一些大数据进行测试,看看它是否对您来说足够快。如果您需要更快的东西,您还可以使用其他模块 - 例如 numpypandas,它们使用 C/C++/Forthran 中的代码。
  • dict 查找是 O(1) ...
  • 在 Python 这样的语言中你永远不会担心这一点,字典查找是该语言中最快的事情之一。如果您尝试在该级别进行优化,请不要使用 Python,请使用 C++。正确地说,每次引用全局变量时,都是在进行字典查找。当您进行属性查找时,例如some_object.some_attribute 您可能正在执行多个字典查找。如果您要使用 Python,请不要担心这一点。
  • 使用可变对象,正如您所说,但请记住,您可能过度优化。你在担心什么系列?
  • 如果您来自像 C++ 这样的语言,那么您对 ​​CPython 的慢速和优化的直觉将是完全错误的。由于高度动态语言的无 JIT 实现的开销,各种操作的运行时成本的所有常量因素都与您习惯的完全不同。

标签: python c++ dictionary lookup


【解决方案1】:

您可以通过使用max(0, ...) 而不是if ... &gt; 0: ... 来避免三种查找之一。

>>> def f(c):
...     if c['a'] > 0:
...         c['a'] -= 1

>>> def g(c):
...     c['a'] = max(0, c['a'] - 1)

>>> import dis
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (c)
              2 LOAD_CONST               1 ('a')
              4 BINARY_SUBSCR
              6 LOAD_CONST               2 (0)
              8 COMPARE_OP               4 (>)
             10 POP_JUMP_IF_FALSE       28

  3          12 LOAD_FAST                0 (c)
             14 LOAD_CONST               1 ('a')
             16 DUP_TOP_TWO
             18 BINARY_SUBSCR
             20 LOAD_CONST               3 (1)
             22 INPLACE_SUBTRACT
             24 ROT_THREE
             26 STORE_SUBSCR
             28 LOAD_CONST               0 (None)
             30 RETURN_VALUE
>>> dis.dis(g)
  2           0 LOAD_GLOBAL              0 (max)
              2 LOAD_CONST               1 (0)
              4 LOAD_FAST                0 (c)
              6 LOAD_CONST               2 ('a')
              8 BINARY_SUBSCR
             10 LOAD_CONST               3 (1)
             12 BINARY_SUBTRACT
             14 CALL_FUNCTION            2
             16 LOAD_FAST                0 (c)
             18 LOAD_CONST               2 ('a')
             20 STORE_SUBSCR
             22 LOAD_CONST               0 (None)
             24 RETURN_VALUE

BINARY_SUBSCR 读取 c['a']STORE_SUBSCR 分配给 c['a']

这是使用Python 3.8.1 (tags/v3.8.1:1b293b6, Dec 18 2019, 23:11:46) [MSC v.1916 64 bit (AMD64)] on win32


但这并不意味着一个或另一个版本更快。您必须进行一些测量才能找出答案。

【讨论】:

  • 您还可以通过将c['a'] 保存到变量并使用c['a'] = saved_value - 1 来避免这种查找。
  • 没错...我没想到这么简单:(
猜你喜欢
  • 2013-03-31
  • 1970-01-01
  • 2015-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多