2012-06-12 40 views
2

我在写一些需要存储和访问不同类型元素的列表的Python代码。此列表中的每个元素都是不同的类类型。例如:Python中的容器列表:标准列表与numpy数组

def file_len(fname): 
    i = 0 
    with open(fname) as f: 
     for i, l in enumerate(f): 
      pass 
    return i + 1 

element_list = [ ] 
data = np.loadtxt(filename) 


if file_len(filename) == 1 : 
      data = np.loadtxt(filename) 
      param1 = data[0] 
      param2 = data[1] 
      element_list.append(Class1.Class1(param1,param2)) 
else: 
    for field in data: 
        param1 = field[0] 
        param2 = field[1] 
        element_list.append(Class1.Class1(param1, param2) 

后来,我将需要访问从插在element_list几次Class1的方法,但不需要列表进行修改:

for i in xrange(10000): 
    for element in element_list: 
     result += element.calculate_result(i).real #the results will be complex 

有没有做一个有效的方法这个?

谢谢!

+1

你的实际问题是什么?以及你的意思是什么_efficient_? – moooeeeep

+0

我必须访问这个列表10亿次或更多,这是_slow_。其中一个瓶颈是访问元素。有没有更加pythonic的做法呢? – Ivan

+0

您可能可以通过对numpy数组'data'执行操作来加速计算。通过将数据分成“Class1.Class1”实例的**列表**,我们失去了利用numpy的机会。但要真正帮助你,我们需要看看'calculate_result'的定义。 – unutbu

回答

1

这不是一个完整的答案,但我发现了两件事我可以做出贡献。

这是file_len()的改进版本。如果文件长度为零,这个将返回0。你的函数对于零长度文件返回1,对于一行文件则返回1。

def file_len(fname): 
    i = 0 
    with open(fname) as f: 
     for i, l in enumerate(f, 1): 
      pass 
    return i 

这是计算循环的一种更快的方法。

result = sum(e.calculate_result(i).real for i in xrange(10000) for e in element_list) 

可能可以让它更快使用reduce(),但我不认为它可以更快。 reduce()节省了大笔费用,如果您可以避免绑定名称一遍又一遍,但我们需要绑定名称e,因此我们可以调用e.calculate_result(i).real,即使e的类型可能是任何类型。

如果你可以做这样的事情,它可能会快一点。

import itertools as it 
import operator as op 
result = reduce(op.add, it.imap(SomeClass.calculate_something, it.product(element_list, xrange(10000)))) 

同样,主要的节省是避免绑定名称。 it.product()返回包含(e, i)的元组,其中e是来自element_list的元素并且i是来自xrange(10000)的数字。然后it.imap()将调用该函数并将该元组作为参数传递。然后reduce()将总结一切。实际上,只需拨打sum()就可以达到reduce(op.add),但您可以尝试两种方法,看看其中一个比另一个稍快。如果你能找出一些合理的SomeClass.calculate_something,那么也许你可以做这个工作。嗯,它可能是值得尝试的,只需让sum()(或reduce())计算一个复数和,然后在完成总和后丢弃虚数部分。这会比每个值访问一次.real属性更快吗?我不确定,但它可以帮助您使reduce()版本正常工作。

编辑:

你应该尝试PyPy下运行您的程序。

http://pypy.org/

如果你这样做,一定要使用这条线,而不是我第一个显示的:

result = sum(e.calculate_result(i).real for e in element_list for i in xrange(10000)) 

这样,您使用的每个元素e在连续10000个电话,这应该可以帮助PyPy即时编译器(“JIT”)生成更好的代码。我不知道JIT是否只能帮助10000个电话,但它似乎应该是尝试它的方法。

0

通过在实例化处传递该视图,可以将结果放入数组的视图中。如果您访问数据的频率比调用类方法更新的次数要多,那么这应该起作用。

像下面这样...

def file_len(fname): 
    i = 0 
    with open(fname) as f: 
     for i, l in enumerate(f): 
      pass 
    return i + 1 

element_list = [ ] 
data = np.loadtxt(filename) 


array_idx = 0 

# length_of_data is the number of elements that will be in element_list 
result_array = numpy.zeros(length_of_data, dtype='complex128') 

if file_len(filename) == 1 : 
    data = np.loadtxt(filename) 
    param1 = data[0] 
    param2 = data[1] 
    element_list.append(Class1.Class1(param1, param2, 
          result_array[array_idx:array_idx+1])) 
    array_idx += 1 
else: 
    for field in data: 
     param1 = field[0] 
     param2 = field[1] 
     element_list.append(Class1.Class1(param1, param2, 
          result_array[array_idx:array_idx+1]) 
     array_idx += 1 

类里面你会然后直接更新视图。考虑这个最简单的例子:

import numpy 

a = numpy.zeros(5, dtype='complex128') 

class Foo(object): 

    def __init__(self, real, imag, array_view): 
     self._array_view = array_view 
     self._array_view[:] = real + 1j*imag #<--- The [:] is needed 


element_list = [] 
for n in range(0, len(a)): 
    element_list.append(Foo(n, n+1, a[n:n+1])) 

print(a) 
print(numpy.sum(a))