Как сделать этот блок кода Python более эффективным - PullRequest
1 голос
/ 19 декабря 2009

Этот блок кода работает - он перебирает файл с повторяющимся числом наборов данных и извлекает каждую из 5 частей информации для каждого набора.

Но я знаю, что текущий факторинг не так эффективен, как может быть, так как он зацикливается через каждый ключ для каждой найденной строки.

Интересно, могут ли некоторые гуру-питоны предложить лучший способ сделать это более эффективно?

def parse_params(num_of_params,lines):

  for line in lines:
    for p in range(1,num_of_params + 1,1):
      nam = "model.paramName "+str(p)+" "
      par = "model.paramValue "+str(p)+" "
      opt = "model.optimizeParam "+str(p)+" "
      low = "model.paramLowerBound "+str(p)+" "
      upp = "model.paramUpperBound "+str(p)+" "
      keys = [nam,par,opt,low,upp]
      for key in keys:
        if key in line:
          a,val = line.split(key)
          if key == nam: names.append(val.rstrip())
          if key == par: params.append(val.rstrip())
          if key == opt: optimize.append(val.rstrip())
          if key == upp: upper.append(val.rstrip())
          if key == low: lower.append(val.rstrip())

print "Names   = ",names   
print "Params   = ",params
print "Optimize = ",optimize
print "Upper    = ",upper
print "Lower    = ",lower

Ответы [ 7 ]

2 голосов
/ 19 декабря 2009

Хотя это не отвечает на ваш вопрос (другие ответы на этот вопрос), что мне очень помогло в выполнении вещей, похожих на то, что вы делаете, это Список понятий . Они позволяют создавать списки в краткой и (я думаю) легко читаемой форме.

Например, приведенный ниже код создает двухмерный массив со значениями, к которым вы пытаетесь получить. some_funct здесь было бы небольшое регулярное выражение, если бы я делал это, которое использует индекс последнего пробела в ключе в качестве параметра и смотрит вперед, чтобы собрать значение, которое вы пытаетесь получить в строке (значение что соответствует ключу, который просматривается в данный момент) и добавляет его к правильному индексу в двумерном массиве seen_keys.

Словесно, да, но если у вас есть понимание списка и вы можете создать регулярное выражение для этого, у вас есть хорошее, краткое решение.

keys = ["model.paramName ","model.paramValue ","model.optimizeParam ""model.paramLowerBound ","model.paramUpperBound "]
for line in lines:
    seen_keys = [[],[],[],[],[]]
    [seen_keys[keys.index(k)].some_funct(line.index(k) for k in keys if k in line]
1 голос
/ 19 декабря 2009

Вы уверены, что parse_params является узким местом? Вы профилировали ваше приложение?

import re
from collections import defaultdict 

names = ("paramName paramValue optimizeParam "
         "paramLowerBound paramUpperBound".split())
stmt_regex = re.compile(r'model\.(%s)\s+(\d+)\s+(.*)' % '|'.join(names))

def parse_params(num_of_params, lines):
    stmts = defaultdict(list)
    for m in (stmt_regex.match(s) for s in lines):
        if m and 1 <= int(m.group(2)) <= num_of_params: 
           stmts[m.group(1)].append(m.group(3).rstrip())

    for k, v in stmts.iteritems():
        print "%s = %s" % (k, ' '.join(v))
1 голос
/ 19 декабря 2009

Код, приведенный в OP, выполняет несколько тестов на строку, чтобы попытаться сопоставить их с ожидаемым набором значений, каждое из которых создается на лету. Вместо того, чтобы создавать paramValue1, paramValue2 и т. Д. Для каждой строки, мы можем использовать регулярное выражение, чтобы попытаться выполнить сопоставление более дешевым (и более надежным) способом.

Вот мой фрагмент кода, опирающийся на некоторые идеи, которые уже были опубликованы. Это позволяет добавить новое ключевое слово в словарь key_to_collection и больше ничего не менять.

import re

def parse_params(num_of_params, lines):

    pattern = re.compile(r"""
        model\.
        (.+)    # keyword
        (\d+)   # index to keyword
        [ ]+    # whitespace
        (.+)    # value
        """, re.VERBOSE)

    key_to_collection = {
        "paramName": names,
        "paramValue": params,
        "optimizeParam": optimize,
        "paramLowerBound": upper,
        "paramUpperBound": lower,
    }

    for line in lines:
        match = pattern.match(line)
        if not match:
            print "Invalid line: " + line
        elif match[1] not in key_to_collection:
            print "Invalid key: " + line
        # Not sure if you really care about enforcing this
        elif match[2] > num_of_params:
            print "Invalid param: " + line
        else:
            key_to_collection[match[1]].append(match[3])

Полное раскрытие: я не скомпилировал / проверил это.

1 голос
/ 19 декабря 2009

Это не совсем очевидно из вашего кода, но похоже, что каждая строка может иметь максимум один "удар"; если это действительно так, то что-то вроде:

import re

def parse_params(num_of_params, lines):
  sn = 'Names Params Optimize Upper Lower'.split()
  ks = '''paramName paramValue optimizeParam
          paramLowerBound paramUpperBound'''.split()
  vals = dict((k, []) for k in ks)
  are = re.compile(r'model\.(%s) (\d+) (.*)' % '|'.join(ks))
  for line in lines:
    mo = are.search(line)
    if not mo: continue
    p = int(mo.group(2))
    if p < 1 or p > num_of_params: continue
    vals[mo.group(1)].append(mo.group(3).rstrip())
  for k, s in zip(ks, sn):
    print '%-8s =' % s,
    print vals[k]

может сработать - я применил это с небольшим кодом следующим образом:

if __name__ == '__main__':
  lines = '''model.paramUpperBound 1 ZAP
    model.paramLowerBound 1 zap
    model.paramUpperBound 5 nope'''.splitlines()
  parse_params(2, lines)

и он излучает

Names    = []
Params   = []
Optimize = []
Upper    = ['zap']
Lower    = ['ZAP']

Я думаю, это то, что вы хотите (если некоторые детали должны отличаться, пожалуйста, укажите точно, что это такое, и давайте посмотрим, сможем ли мы это исправить).

Две ключевые идеи: используйте dict вместо множества if s; используйте re, чтобы сопоставить «любую из следующих возможностей» с заключенными в скобки группами в шаблоне re, чтобы поймать интересующие биты (ключевое слово после model., целое число после этого и «значение», являющееся остальными линии) вместо множества if x in y проверок и манипуляций со строками.

1 голос
/ 19 декабря 2009

Не совсем легко увидеть ожидаемый формат. Из того, что я вижу, формат такой:

lines = [
    "model.paramName 1 foo",
    "model.paramValue 2 bar",
    "model.optimizeParam 3 bat",
    "model.paramLowerBound 4 zip",
    "model.paramUpperBound 5 ech",
    "model.paramName 1 foo2",
    "model.paramValue 2 bar2",
    "model.optimizeParam 3 bat2",
    "model.paramLowerBound 4 zip2",
    "model.paramUpperBound 5 ech2",
]

Я не вижу работающего кода, если в каждой строке более одного значения. Что означает, что цифра не очень важна, если я что-то упустил. В этом случае это работает очень легко:

import re

def parse_params(num_of_params,lines):
    key_to_collection = {
        "model.paramName":names,
        "model.paramValue":params,
        "model.optimizeParam":optimize,
        "model.paramLowerBound":upper,
        "model.paramUpperBound":lower,
    }

    reg = re.compile(r'(.+?) (\d) (.+)')

    for line in lines:
        m = reg.match(line)
        key, digit, value = m.group(1, 2, 3)
        key_to_collection[key].append(value)
1 голос
/ 19 декабря 2009

Там много дублирования, и если вы когда-нибудь добавите еще key или param, вам придется добавлять его во многих местах, что оставляет вас готовыми к ошибкам. То, что вы хотите сделать, это сократить все места, где вы повторяли вещи, и использовать какую-то модель данных, такую ​​как dict.

Некоторые другие предоставили несколько превосходных примеров, поэтому я просто оставлю здесь свой ответ, чтобы дать вам кое-что для размышления.

0 голосов
/ 19 декабря 2009

Это, безусловно, можно сделать более эффективным. Но, честно говоря, разве это необходимо, если эта функция вызывается сотни раз в секунду или работает на тысячах строк?

Я бы больше хотел прояснить, что происходит ... в настоящее время я далек от понимания этого аспекта.

Просто следите за этим, вход выглядит так:

model.paramName 1 A model.paramValue 1 B model.optimizeParam 1 C model.paramLowerBound 1 D model.paramUpperBound 1 E model.paramName 2 F model.paramValue 2 G model.optimizeParam 2 H модель. Я модель.paramUpperBound 2 J

И ваш желаемый результат выглядит примерно так:

Names     = AF
Params    = BG
etc...

Теперь, поскольку мои входные данные, конечно, не совпадают с вашими, выходные данные, вероятно, тоже отключены, но я думаю, что у меня есть суть.

Есть несколько моментов. Во-первых, имеет ли значение, сколько параметров передается в функцию? Например, если вход имеет два набора параметров, я хочу просто прочитать оба, или необходимо разрешить функции читать только один? Например, ваш код позволяет мне вызывать parse_params(1,1), и он может считывать только те параметры, которые заканчиваются на 1 из того же ввода Если это не является обязательным требованием, вы можете пропустить большую часть кода.

Во-вторых, важно ли ТОЛЬКО читать данные параметры? Если у меня, например, есть параметр с именем 'paramFoo', плохо ли это, если я его читаю? Вы также можете упростить процедуру, просто захватывая все параметры независимо от их имени и извлекая их значение.

def parse_params(input):
  parameter_list = {}
  param = re.compile(r"model\.([^ ]+) [0-9]+ ([^ ]+)")
  each_parameter = param.finditer(input)
  for match in each_parameter:
    key = match[0]
    value = match[1]
    if not key in paramter_list:
      parameter_list[key] = []

    parameter_list[key].append(value)

  return parameter_list

В этом случае вывод будет выглядеть примерно так:

{'paramName': [A, F], 'paramValue': [B, G], 'optimizeParam': [C, H] и т. Д ...}

Примечания: Я плохо знаю Python, я парень из Ruby, поэтому мой синтаксис может быть отключен. Извинения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...