избягване на дублиране на код в Python code 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')

Възможно ли е да се компактифицира този код? Ако имам някои общи неща в два цикъла като този, една очевидна възможност е просто да отделя общия код, но тук, eww. Дразнещото е, че единствената разлика тук е писането на 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-190ns/повикване моята система, CPython+Psyco дава ~9-35ns/обаждане. - 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