2012-06-12 3 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) 

Позже мне нужно будет получить доступ к методам из Class1 вставленной в element_list несколько раз, но список не нужно быть изменен:

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

Мне нужно получить доступ к этому списку в миллиард раз или более, и это _slow_. Одним из узких мест является доступ к элементам. Есть ли более питонический способ сделать это? – Ivan

+0

Возможно, вы сможете ускорить вычисление, выполнив операции над массивом «data» numpy. Разделив данные в ** список ** экземпляров 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().

EDIT:

Вы должны попробовать запустить программу под 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)) 
Смежные вопросы