#python #code-duplication #control-flow
#python #дублирование кода #поток управления
Вопрос:
Это продолжение предыдущего вопроса. У меня есть несколько хороших предложений по этому поводу, поэтому я подумал, что попробую свою удачу еще раз.
from itertools import takewhile
if K is None:
illuminacond = lambda x: x.split(',')[0] != '[Controls]'
else:
illuminacond = lambda x: x.split(',')[0] != '[Controls]' and i < K
af=open('a')
bf=open('b', 'w')
cf=open('c', 'w')
i = 0
if K is None:
for line in takewhile(illuminacond, af):
line_split=line.split(',')
pid=line_split[1][0:3]
out = line_split[1] ',' line_split[2] ',' line_split[3][1] line_split[3][3] ','
line_split[15] ',' line_split[9] ',' line_split[10]
if pid!='cnv' and pid!='hCV' and pid!='cnv':
i = i 1
bf.write(out.strip('"') 'n')
cf.write(line)
else:
for line in takewhile(illuminacond, af):
line_split=line.split(',')
pid=line_split[1][0:3]
out = line_split[1] ',' line_split[2] ',' line_split[3][1] line_split[3][3] ','
line_split[15] ',' line_split[9] ',' line_split[10]
if pid!='cnv' and pid!='hCV' and pid!='cnv':
i = i 1
bf.write(out.strip('"') 'n')
Возможно ли компактировать этот код? Если у меня есть что-то общее в двух циклах, подобных этому,
одна очевидная возможность — просто исключить общий код, но здесь, фу.
Раздражает то, что единственным отличием здесь является запись в c
.
Краткое описание кода: Если K
не равно None, то выполните цикл по K
строкам a
и запишите в оба b
и c
. В противном случае выполните цикл по всему a
и просто напишите в b
.
Ответ №1:
Почему бы не использовать только один цикл, но включить условие внутри этого цикла? Кроме того, я думаю, вы можете избавиться от избыточности в этом лямбда-выражении.
from itertools import takewhile
k_is_none = K is None
def illuminacond(x):
global i
global K
result = x.split(',')[0] != '[Controls]'
if not k_is_none:
result = result and i < K
return result
af=open('a')
bf=open('b', 'w')
cf=open('c', 'w')
i = 0
for line in takewhile(illuminacond, af):
line_split=line.split(',')
pid=line_split[1][0:3]
out = line_split[1] ',' line_split[2] ',' line_split[3][1] line_split[3][3] ','
line_split[15] ',' line_split[9] ',' line_split[10]
if pid!='cnv' and pid!='hCV' and pid!='cnv':
i = i 1
bf.write(out.strip('"') 'n')
if k_is_none:
cf.write(line)
Комментарии:
1. Условие
K is None
нужно будет проверять по крайней мереK
раз. В остальном, нет, ничего плохого. Конечно, в этом случае проверка не требует много времени, но предположим, что это было?2. Просто кэшируйте его следующим образом:
my_cond = K is None
3. Теперь вы выполняете не одну, а две проверки во время выполнения для
K
. 🙂4. @faheem-mitha Пока @brian-odell печатал свой ответ, я редактировал предложенный мной код. Если вас беспокоит это сравнение, сделайте это один раз и сохраните результат.
k_is_none = K is None
Ответ №2:
Одна проверка, один цикл, никаких классов, оптимизируемый для psyco.
from itertools import takewhile
if K is None:
illuminacond = lambda x: x.split(',')[0] != '[Controls]'
def action(cf, line): cf.write(line)
else:
illuminacond = lambda x: x.split(',')[0] != '[Controls]' and i < K
def action(cf, line): pass
af=open('a')
bf=open('b', 'w')
cf=open('c', 'w')
i = 0
for line in takewhile(illuminacond, af):
line_split=line.split(',')
pid=line_split[1][0:3]
out = line_split[1] ',' line_split[2] ',' line_split[3][1] line_split[3][3] ','
line_split[15] ',' line_split[9] ',' line_split[10]
if pid!='cnv' and pid!='hCV' and pid!='cnv':
i = i 1
bf.write(out.strip('"') 'n')
action(cf, line)
Комментарии:
1. Эй, неплохо! Какова здесь стоимость вызова функции?
2. Кроме того, заинтригован ссылкой на psyco. Хотя я почти ничего не знаю о psyco. Что означает, что код Python должен быть оптимизируемым для psyco?
3. Стоимость вызова функции сильно зависит от версии python / arch. Psyco — оптимизатор python (к сожалению, на данный момент не поддерживается в python> 2.6). Я провел некоторые измерения, используя этот скрипт , чистые вызовы функций CPython в моей системе составляют ~ 170-190ns / вызов, CPython Psyco выдает ~ 9-35ns / вызов.
4. Спасибо, это очень интересная и полезная информация. Я использую Python 2.6 в Debian, поэтому я мог видеть, насколько велика разница при использовании Psyco.
Ответ №3:
Почему бы просто:
from itertools import takewhile
illuminacond = lambda x: x.split(',')[0] != '[Controls]' and (K is None or i<K) #i'm not so sure about this part, confused me a little :).
af=open('a')
bf=open('b', 'w')
cf=open('c', 'w')
for line in takewhile(illuminacond, af):
line_split=line.split(',')
pid=line_split[1][0:3]
out = line_split[1] ',' line_split[2] ',' line_split[3][1] line_split[3][3] ','
line_split[15] ',' line_split[9] ',' line_split[10]
if pid!='cnv' and pid!='hCV' and pid!='cnv':
i = i 1
bf.write(out.strip('"') 'n')
if K is None:
cf.write(line)
Ответ №4:
Как насчет этого (версия на основе второго класса)?
from itertools import takewhile
class Foo:
def __init__(self, K = None):
self.bf=open('b', 'w')
self.cf=open('c', 'w')
self.count = 0
self.K = K
def Go(self):
for self.line in takewhile(self.Lamda(), open('a')):
self.SplitLine()
if self.IsValidPid():
self.WriteLineToFiles()
def SplitLine(self):
self.lineSplit=self.line.split(',')
def Lamda(self):
if self.K is None:
return lambda x: x.split(',')[0] != '[Controls]'
else:
return lambda x: x.split(',')[0] != '[Controls]' and self.count < self.K
def IsValidPid(self):
pid=self.lineSplit[1][0:3]
return pid!='cnv' and pid!='hCV' and pid!='cnv'
def WriteLineToFiles(self):
self.count = 1
self.bf.write(self.ParseLine())
if self.K is None:
self.cf.write(self.line)
def ParseLine(self):
return (self.lineSplit[1] ',' self.lineSplit[2] ','
self.lineSplit[3][1] self.lineSplit[3][3] ','
self.lineSplit[15] ',' self.lineSplit[9] ','
self.lineSplit[10]).strip('"') 'n'
Foo().Go()
Оригинальная версия:
from itertools import takewhile
if K is None:
illuminacond = lambda x: x.split(',')[0] != '[Controls]'
else:
illuminacond = lambda x: x.split(',')[0] != '[Controls]' and i < K
def Parse(line):
return (line[1] ',' line[2] ',' line[3][1] line[3][3] ','
line[15] ',' line[9] ',' line[10]).strip('"') 'n'
def IsValidPid(line_split):
pid=line_split[1][0:3]
return pid!='cnv' and pid!='hCV' and pid!='cnv'
bf=open('b', 'w')
cf=open('c', 'w')
def WriteLineToFiles(line, line_split):
bf.write(Parse(line_split))
if K is None:
cf.write(line)
i = 0
for line in takewhile(illuminacond, open('a')):
line_split=line.split(',')
if IsValidPid(line_split):
WriteLineToFiles(line, line_split)
i = 1
Комментарии:
1. Правильно, это то, что я имел в виду под «вычленением общего кода» в моем вопросе выше. Это, безусловно, один из способов.
2. Обычно я начинаю с одного уровня абстракции, группирую вместе фрагменты кода, которые имеют общий интерес, раскладываю по методам и даю им подходящие названия. Комментарии не требуются, и код де-факто документируется самостоятельно. Это очень итеративно и инкрементно по своей природе, так и должно быть 🙂
3. Добавлена версия на основе второго класса, которая немного больше инкапсулирует и обеспечивает лучшую абстракцию. Вы сразу поймете, что делает код, проверив Go().
4. 1 за усилия, но я не большой поклонник подхода к вещам с использованием методов класса, если в этом нет абсолютной необходимости.
5. @Faheem: Ну, вы можете выбрать любой из них, мне все равно : D Необходимость может обсуждаться на многих уровнях. Лично я считаю, что чем чище код, тем меньше вам нужно его поддерживать и отлаживать. Вы выигрываете в долгосрочной перспективе. Было бы здорово, если бы кто-нибудь мог продолжить его рефакторинг 🙂