избежание дублирования кода в коде Python Redux

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

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.


person Faheem Mitha    schedule 03.05.2011    source источник


Ответы (4)


Одна проверка, один цикл, никаких классов, психооптимизация.

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)
person abbot    schedule 03.05.2011
comment
Эй, не плохо! Какова здесь стоимость вызова функции? - person Faheem Mitha; 04.05.2011
comment
Кроме того, заинтригован отсылкой к психологу. Хотя почти ничего не знает о психологии. Что означает, что код Python может быть психооптимизируемым? - person Faheem Mitha; 04.05.2011
comment
Стоимость вызова функции сильно зависит от версии/архитектуры Python. Psyco — оптимизатор Python (к сожалению, пока не поддерживается в Python › 2.6). Я сделал некоторые измерения, используя этот скрипт, вызовы функций чистого CPython составляют ~170-190 нс/вызов на моя система CPython + Psyco дает ~ 9-35 нс / вызов. - person abbot; 04.05.2011
comment
Спасибо, очень интересная и полезная информация. Я запускаю Python 2.6 на Debian, поэтому я мог видеть, насколько большую разницу дает использование Psyco. - person Faheem Mitha; 04.05.2011

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

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)
person arussell84    schedule 03.05.2011
comment
Условие K is None нужно будет проверить не менее K раз. В остальном нет, ничего страшного. Конечно, в этом случае проверка не занимает много времени, но предположим, что это было? - person Faheem Mitha; 03.05.2011
comment
Просто кешируйте вот так: my_cond = K is None - person Brian O'Dell; 03.05.2011
comment
Теперь вы выполняете не одну, а две проверки во время выполнения для K. :-) - person Faheem Mitha; 03.05.2011
comment
@faheem-mitha Пока @brian-odell печатал свой ответ, я редактировал предложенный код. Если вас беспокоит это сравнение, сделайте это один раз и сохраните результат. k_is_none = K is None - person arussell84; 03.05.2011

Почему бы просто не:

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)
person utdemir    schedule 03.05.2011

Как насчет этого (версия на основе второго класса)?

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
person ralphtheninja    schedule 03.05.2011
comment
Правильно, это то, что я имел в виду, говоря об общем коде в моем вопросе выше. Это, безусловно, один из способов. - person Faheem Mitha; 04.05.2011
comment
Обычно я начинаю с одного уровня абстракции, группирую куски кода, которые имеют общий интерес, разбиваю на методы и даю им хорошие имена. Комментарии не нужны, код де-факто самодокументирован. Он очень итеративный и постепенный по своей природе, как и должно быть :) - person ralphtheninja; 04.05.2011
comment
Добавлена ​​вторая версия на основе классов, которая немного больше инкапсулирует вещи и обеспечивает лучшую абстракцию. Вы сразу понимаете, что делает код, проверив Go(). - person ralphtheninja; 04.05.2011
comment
+1 за усилия, но я не большой поклонник подхода методов класса к вещам, если в этом нет крайней необходимости. - person Faheem Mitha; 04.05.2011
comment
@Faheem: Ну, ты можешь выбрать любого из них, мне все равно :D Необходимость можно обсуждать на многих уровнях. Лично я считаю, что чем чище код, тем меньше вам нужно его поддерживать и отлаживать. Вы выигрываете в долгосрочной перспективе. Было бы мило, если бы кто-то мог продолжить рефакторинг :) - person ralphtheninja; 04.05.2011