创建一个巨大的查找表,关于性能的问题

时间:2014-09-05 07:38:31

标签: python performance python-3.x lookup-tables

我想创建一个查找表,为此我正在考虑使用字典。字典将具有对应于整数的键(或者在我的情况下对应于类Enum中的枚举类型),并且值将是2,3或4个numpy数组。但不知怎的,我不愿意使用这种方法,因为这本词典有大量的信息,其中99%可能根本不用于某些问题。那么构建包含所有查找信息的单个对象是没有意义的,即使我在猜测,我几乎可以肯定,有更好的方法来实现我想要的要做。

所以来自C ++世界,我会为函数指针创建一个unordered_map枚举类型,在函数中我将创建一个static数组(这样它只会被创建一次)然后我会返回数组指针。通过这种方式,我只会实例化查找表中实际需要的部分,而不是整个程序。

但我尝试在Python中做类似的事情,所以我想知道实现这一目标的最有效方法。

修改

所以这就是我到目前为止所提出的。我混合了@AaronDigulla和@DanielRoseman提出的建议,尽管可能不再需要@runoncedict的子类重写__getitem__方法并检查字典中是否存在密钥。如果它没有,它会调用一个函数(在字典键值的连接字符串上使用eval())。我将不胜感激给定代码的任何改进。它看起来很复杂,但它确实有效,所以我想知道它是否可以进一步简化。

import collections, types
import numpy as np

Quadrature = collections.namedtuple("Quadrature", "wgt xi eta zeta")

category_map = { "t3" : "tri" #... more types
               }


class Integrator(dict):

  def __init__(self, *args, **kwargs):
    self.update(*args, **kwargs)

  def __getitem__(self, key):

    if not key in self:

      fn = '{}_{}gp'.format(category_map[key[0]], str(key[1]))
      super().__setitem__(key, eval(fn)())

    val = super().__getitem__(key)
    return val

  def __repr__(self):
    dictrepr = dict.__repr__(self)
    return '%s(%s)' % (type(self).__name__, dictrepr)

  def update(self, *args, **kwargs):
    print ('update', args, kwargs)
    for k, v in dict(*args, **kwargs).items():
        self[k] = v

def run_once(f):
  def wrapper(*args, **kwargs):
    if not wrapper.has_run:
      wrapper.has_run = True
      return f(*args, **kwargs)
  wrapper.has_run = False
  return wrapper


@run_once
def tri_3gp():
  xi   = np.array([2/3., 1/6., 1/6.])
  eta  = np.array([1/6., 1/6., 2/3.])
  wgt  = np.array([2/3., 2/3., 2/3.]);
  return Quadrature(wgt, xi, eta, None)

2 个答案:

答案 0 :(得分:1)

你可以用Python做同样的事情。实际上,它更容易,因为函数本身就是一流的对象:您可以将它们存储在字典中并根据需要调用它们。

要替换静态数组,可以使用某种记忆,例如标准的全局查找数组。

global_dict = {}

def func1():
    if 'param1' not in global_dict:
        global_dict['param1'] = my_complicated_function_for_param_1()
    return global_dict['param1'] = my_complicated_function_for_param_1()



lookup_dict = {
    'param1': func1,
    ...
}

# now do the lookup:
my_result = lookup_dict[my_param]()

你当然可能想要从计算函数中分解逻辑:装饰器可能是一个很好的方法。

答案 1 :(得分:1)

在Python中这很简单。看到这个问题如何创建"运行一次"装饰者:Efficient way of having a function only execute once in a loop

现在您可以使用函数在地图中创建数据。装饰者将确保它们最多运行一次(第一次调用它们)。然后循环就像这样:

@run_once
def funcForKey():
    ...

lookup_dict = {
    'key': funcForKey,
    ...
}

result = lookup_dict[x]()

()调用该函数后的[]

您也可以尝试上课:

class Data(object):
    @run_once
    def key(self):
        ...

data = Data()

现在您可以查找如下值:

a = 'key'
result = getattr(data, a)()

或者,如果名称在运行时是常量,则只需:

result = data.key()