Решение без itertools.groupby() :
p = ['**', 'foo', '*', 'bar', 'bar', '**', '**', '**', 'baz', '**', '**',
'foo', '*','*', 'bar', 'bar','bar', '**', '**','foo','bar',]
def treat(A):
prec = A[0]; yield prec
for x in A[1:]:
if (prec,x)!=('**','**'): yield x
prec = x
print p
print
print list(treat(p))
результат
['**', 'foo', '*', 'bar', 'bar', '**', '**', '**',
'baz', '**', '**',
'foo', '*', '*', 'bar', 'bar','bar', '**', '**',
'foo', 'bar']
['**', 'foo', '*', 'bar', 'bar', '**',
'baz', '**',
'foo', '*', '*', 'bar', 'bar', 'bar', '**',
'foo', 'bar']
Другое решение, вдохновленное dugres
from itertools import groupby
p = ['**', 'foo', '*', 'bar', 'bar', '**', '**', '**', 'baz', '**', '**',
'foo', '*','*', 'bar', 'bar','bar', '**', '**','foo','bar',]
res = []
for k, g in groupby(p):
res.extend( ['**'] if k=='**' else list(g) )
print res
Это похоже на решение Тома Зыка, но проще
.
РЕДАКТИРОВАТЬ
p = ['**','**', 'foo', '*', 'bar', 'bar', '**', '**', '**', 'baz', '**', '**',
'foo', '*','*', 'bar', 'bar','bar', '**', '**','foo','bar', '**', '**', '**']
q= ['**',12,'**',45, 'foo',78, '*',751, 'bar',4789, 'bar',3, '**', 5,'**',7, '**',
73,'baz',4, '**',8, '**',20,'foo', 8,'*',36,'*', 36,'bar', 11,'bar',0,'bar',9,
'**', 78,'**',21,'foo',27,'bar',355, '**',33, '**',37, '**','end']
def treat(B,dedupl):
B = iter(B)
prec = B.next(); yield prec
for x in B:
if not(prec==x==dedupl): yield x
prec = x
print 'gen = ( x for x in q[::2])'
gen = ( x for x in q[::2])
print 'list(gen)==p is ',list(gen)==p
gen = ( x for x in q[::2])
print 'list(treat(gen)==',list(treat(gen,'**'))
ch = '??h4i4???4t4y?45l????hmo4j5???'
print '\nch==',ch
print "''.join(treat(ch,'?'))==",''.join(treat(ch,'?'))
print "\nlist(treat([],'%%'))==",list(treat([],'%%'))
результат
gen = ( x for x in q[::2])
list(gen)==p is True
list(treat(gen)== ['**', 'foo', '*', 'bar', 'bar', '**', 'baz', '**', 'foo', '*', '*', 'bar', 'bar', 'bar', '**', 'foo', 'bar', '**']
ch== ??h4i4???4t4y?45l????hmo4j5???
''.join(treat(ch,'?'))== ?h4i4?4t4y?45l?hmo4j5?
list(treat([],'%%'))== []
.
Примечание: функция-генератор позволяет адаптировать вывод к типу ввода с записью вокруг вызова генератора, не требует изменения внутреннего кода функции-генератора;
Это не относится к решению Тома Зинча, которое нельзя так легко адаптировать к типу ввода.
.
РЕДАКТИРОВАТЬ 2
Я искал однострочный метод с пониманием списка или выражением генератора.
Я нашел способы сделать это, я думаю, что без groupby() не обойтись.
from itertools import groupby
from operator import concat
p = ['**', '**','foo', '*', 'bar', 'bar', '**', '**', '**',
'bar','**','foo','sun','sun','sun']
print 'p==',p,'\n'
dedupl = ("**",'sun')
print 'dedupl==',repr(dedupl)
print [ x for k, g in groupby(p) for x in ((k,) if k in dedupl else g) ]
# or
print reduce(concat,( [k] if k in dedupl else list(g) for k, g in groupby(p)),[])
По тому же принципу легко преобразовать функцию дюгреса в функцию генератора:
from itertools import groupby
def compress(iterable, to_compress):
for k, g in groupby(iterable):
if k in to_compress:
yield k
else:
for x in g: yield x
Однако у этой генераторной функции есть два недостатка:
он прибегает к функции groupby(), которую нелегко понять тому, кто не привык к Python.
время ее выполнения больше, чем у моей генераторной функции treat() и генераторной функции Джона Мачина, которые не используют groupby().
Я немного модифицировал их, чтобы они могли принимать последовательность элементов для дедупликации, и измерил продолжительность выполнения:
from time import clock
from itertools import groupby
def squeeze(iterable, victims, _dummy=object()):
if hasattr(iterable, '__iter__') and not hasattr(victims, '__iter__'):
victims = (victims,)
previous = _dummy
for item in iterable:
if item in victims and item==previous:
continue
previous = item
yield item
def treat(B,victims):
if hasattr(B, '__iter__') and not hasattr(victims, '__iter__'):
victims = (victims,)
B = iter(B)
prec = B.next(); yield prec
for x in B:
if x not in victims or x!=prec: yield x
prec = x
def compress(iterable, to_compress):
if hasattr(iterable, '__iter__') and not hasattr(to_compress, '__iter__'):
to_compress = (to_compress,)
for k, g in groupby(iterable):
if k in to_compress:
yield k
else:
for x in g: yield x
p = ['**', '**','su','foo', '*', 'bar', 'bar', '**', '**', '**',
'su','su','**','bin', '*','*','bar','bar','su','su','su']
n = 10000
te = clock()
for i in xrange(n):
a = list(compress(p,('**','sun')))
print clock()-te,' generator function with groupby()'
te = clock()
for i in xrange(n):
b = list(treat(p,('**','sun')))
print clock()-te,' generator function eyquem'
te = clock()
for i in xrange(n):
c = list(squeeze(p,('**','sun')))
print clock()-te,' generator function John Machin'
print p
print 'a==b==c is ',a==b==c
print a
Инструкция
if hasattr(iterable, '__iter__') and not hasattr(to_compress, '__iter__'):
to_compress = (to_compress,)
необходимо, чтобы избежать ошибок, когда итерируемый аргумент представляет собой последовательность, а другой аргумент — только одну строку: этот последний затем необходимо преобразовать в контейнер, при условии, что итерируемый аргумент не является самой строкой.
Он основан на том факте, что последовательности, такие как кортежи, списки, сте... имеют метод iter, а строки — нет. Следующий код показывает проблемы:
def compress(iterable, to_compress):
if hasattr(iterable, '__iter__') and not hasattr( to_compress, '__iter__'):
to_compress = (to_compress,)
print 't_compress==',repr(to_compress)
for k, g in groupby(iterable):
if k in to_compress:
yield k
else:
for x in g: yield x
def compress_bof(iterable, to_compress):
if not hasattr(to_compress, '__iter__'): # to_compress is a string
to_compress = (to_compress,)
print 't_compress==',repr(to_compress)
for k, g in groupby(iterable):
if k in to_compress:
yield k
else:
for x in g: yield x
def compress_bug(iterable, to_compress_bug):
print 't_compress==',repr(to_compress_bug)
for k, g in groupby(iterable):
#print 'k==',k,k in to_compress_bug
if k in to_compress_bug:
yield k
else:
for x in g: yield x
q = ';;;htr56;but78;;;;$$$$;ios4!'
print 'q==',q
dedupl = ";$"
print 'dedupl==',repr(dedupl)
print
print "''.join(compress (q,"+repr(dedupl)+")) :\n",''.join(compress (q,dedupl))+\
' <-CORRECT ONE'
print
print "''.join(compress_bof(q,"+repr(dedupl)+")) :\n",''.join(compress_bof(q,dedupl))+\
' <====== error ===='
print
print "''.join(compress_bug(q,"+repr(dedupl)+")) :\n",''.join(compress_bug(q,dedupl))
print '\n\n\n'
q = [';$', ';$',';$','foo', ';', 'bar','bar',';',';',';','$','$','foo',';$12',';$12']
print 'q==',q
dedupl = ";$12"
print 'dedupl==',repr(dedupl)
print
print 'list(compress (q,'+repr(dedupl)+')) :\n',list(compress (q,dedupl)),\
' <-CORRECT ONE'
print
print 'list(compress_bof(q,'+repr(dedupl)+')) :\n',list(compress_bof(q,dedupl))
print
print 'list(compress_bug(q,'+repr(dedupl)+')) :\n',list(compress_bug(q,dedupl)),\
' <====== error ===='
print
результат
q== ;;;htr56;but78;;;;$$$$;ios4!
dedupl== ';$'
''.join(compress (q,';$')) :
t_compress== ';$'
;htr56;but78;$;ios4! <-CORRECT ONE
''.join(compress_bof(q,';$')) :
t_compress== (';$',)
;;;htr56;but78;;;;$$$$;ios4! <====== error ====
''.join(compress_bug(q,';$')) :
t_compress== ';$'
;htr56;but78;$;ios4!
q== [';$', ';$', ';$', 'foo', ';', 'bar', 'bar', ';', ';', ';', '$', '$', 'foo', ';$12', ';$12']
dedupl== ';$12'
list(compress (q,';$12')) :
t_compress== (';$12',)
[';$', ';$', ';$', 'foo', ';', 'bar', 'bar', ';', ';', ';', '$', '$', 'foo', ';$12'] <-CORRECT ONE
list(compress_bof(q,';$12')) :
t_compress== (';$12',)
[';$', ';$', ';$', 'foo', ';', 'bar', 'bar', ';', ';', ';', '$', '$', 'foo', ';$12']
list(compress_bug(q,';$12')) :
t_compress== ';$12'
[';$', 'foo', ';', 'bar', 'bar', ';', '$', 'foo', ';$12'] <====== error ====
Я получил следующее время выполнения:
0.390163274941 generator function with groupby()
0.324547114228 generator function eyquem
0.310176572721 generator function John Machin
['**', '**', 'su', 'foo', '*', 'bar', 'bar', '**', '**', '**', 'su', 'su', '**', 'bin', '*', '*', 'bar', 'bar', 'su', 'su', 'su']
a==b==c is True
['**', 'su', 'foo', '*', 'bar', 'bar', '**', 'su', 'su', '**', 'bin', '*', '*', 'bar', 'bar', 'su', 'su', 'su']
Я предпочитаю решение Джона Мачина, потому что здесь нет инструкции B = iter(B), как в моей.
А вот инструкция previous = _dummy с _dummy = object() мне кажется странной. Итак, я, наконец, думаю, что лучшим решением является следующий код, который работает даже со строкой в качестве итерируемого аргумента и в котором первый ранее определенный объект не является подделкой:
def squeeze(iterable, victims):
if hasattr(iterable, '__iter__') and not hasattr(victims, '__iter__'):
victims = (victims,)
for item in iterable:
previous = item
break
for item in iterable:
if item in victims and item==previous:
continue
previous = item
yield item
.
РЕДАКТИРОВАТЬ 3
Я не понял, что object() используется в качестве часового.
Но меня озадачил тот факт, что object называется CALLED. Вчера я подумал, что object — это что-то настолько необычное, что невозможно, чтобы object был в любом итерируемом объекте, переданном в качестве аргумента функции сжатия(). Итак, мне было интересно, почему вы назвали его Джоном Мачином, и это посеяло во мне сомнения относительно его природы; вот почему я попросил вас подтвердить, что object является суперметаклассом.
Но сегодня я, кажется, понял, почему в вашем коде вызывается object.
На самом деле вполне возможно, что object находится в итерации, почему бы и нет? Суперметакласс object — это объект, поэтому ничто не мешает поместить его в итерируемый объект до того, как на нем будет обработана дедупликация, кто знает? Тогда использование самого object в качестве часового является неправильной практикой.
.
Таким образом, вы использовали не object, а экземпляр object() в качестве часового.
Но мне было интересно, зачем выбирать эту загадочную вещь, что возврат вызова к object есть?
Мои размышления продолжались по этому поводу, и я заметил кое-что, что должно быть причиной этого звонка:
вызов object создает экземпляр , поскольку object является самым базовым классом в Python, и каждый раз, когда создается экземпляр, это другой объект, отличный от любого ранее созданного экземпляра, с значение всегда отличается от значения любого предыдущего экземпляра object:
a = object()
b = object()
c = object()
d = object()
print id(a),'\n',id(b),'\n',id(c),'\n',id(d)
print a==b,a==c,a==d
print b==c,b==d,c==d
результат
10818752
10818760
10818768
10818776
False False False
False False False
Таким образом, _dummy=object() является уникальным объектом, имеющим уникальный id и уникальное значение. Кстати, интересно, какова ценность экземпляра object. В любом случае следующий код показывает проблему с _dummy=object и отсутствие проблемы с _dummy=object()
def imperfect_squeeze(iterable, victim, _dummy=object):
previous = _dummy
print 'id(previous) ==',id(previous)
print 'id(iterable[0])==',id(iterable[0])
for item in iterable:
if item in victim and item==previous: continue
previous = item; yield item
def squeeze(iterable, victim, _dummy=object()):
previous = _dummy
print 'id(previous) ==',id(previous)
print 'id(iterable[0])==',id(iterable[0])
for item in iterable:
if item in victim and item==previous: continue
previous = item; yield item
wat = object
li = [wat,'**','**','foo',wat,wat]
print 'imperfect_squeeze\n''li before ==',li
print map(id,li)
li = list(imperfect_squeeze(li,[wat,'**']))
print 'li after ==',li
print
wat = object()
li = [wat,'**','**','foo',wat,wat]
print 'squeeze\n''li before ==',li
print map(id,li)
li = list(squeeze(li,[wat,'**']))
print 'li after ==',li
print
li = [object(),'**','**','foo',object(),object()]
print 'squeeze\n''li before ==',li
print map(id,li)
li = list(squeeze(li,[li[0],'**']))
print 'li after ==',li
результат
imperfect_squeeze
li before == [<type 'object'>, '**', '**', 'foo', <type 'object'>, <type 'object'>]
[505317320, 18578968, 18578968, 13208848, 505317320, 505317320]
id(previous) == 505317320
id(iterable[0])== 505317320
li after == ['**', 'foo', <type 'object'>]
squeeze
li before == [<object object at 0x00A514C8>, '**', '**', 'foo', <object object at 0x00A514C8>, <object object at 0x00A514C8>]
[10818760, 18578968, 18578968, 13208848, 10818760, 10818760]
id(previous) == 10818752
id(iterable[0])== 10818760
li after == [<object object at 0x00A514C8>, '**', 'foo', <object object at 0x00A514C8>]
squeeze
li before == [<object object at 0x00A514D0>, '**', '**', 'foo', <object object at 0x00A514D8>, <object object at 0x00A514E0>]
[10818768, 18578968, 18578968, 13208848, 10818776, 10818784]
id(previous) == 10818752
id(iterable[0])== 10818768
li after == [<object object at 0x00A514D0>, '**', 'foo', <object object at 0x00A514D8>, <object object at 0x00A514E0>]
Проблема заключается в отсутствии <type 'object'> в качестве первого элемента списка после обработки методом imperfect_squeeze() .
Тем не менее, мы должны отметить, что "проблема" возможна только со списком, ПЕРВЫМ элементом которого является object: много размышлений о такой крошечной вероятности.... но строгий кодировщик принимает во внимание все.
Если мы используем list вместо object, результаты будут другими:
def imperfect_sqlize(iterable, victim, _dummy=list):
previous = _dummy
print 'id(previous) ==',id(previous)
print 'id(iterable[0])==',id(iterable[0])
for item in iterable:
if item in victim and item==previous: continue
previous = item; yield item
def sqlize(iterable, victim, _dummy=list()):
previous = _dummy
print 'id(previous) ==',id(previous)
print 'id(iterable[0])==',id(iterable[0])
for item in iterable:
if item in victim and item==previous: continue
previous = item; yield item
wat = list
li = [wat,'**','**','foo',wat,wat]
print 'imperfect_sqlize\n''li before ==',li
print map(id,li)
li = list(imperfect_sqlize(li,[wat,'**']))
print 'li after ==',li
print
wat = list()
li = [wat,'**','**','foo',wat,wat]
print 'sqlize\n''li before ==',li
print map(id,li)
li = list(sqlize(li,[wat,'**']))
print 'li after ==',li
print
li = [list(),'**','**','foo',list(),list()]
print 'sqlize\n''li before ==',li
print map(id,li)
li = list(sqlize(li,[li[0],'**']))
print 'li after ==',li
результат
imperfect_sqlize
li before == [<type 'list'>, '**', '**', 'foo', <type 'list'>, <type 'list'>]
[505343304, 18578968, 18578968, 13208848, 505343304, 505343304]
id(previous) == 505343304
id(iterable[0])== 505343304
li after == ['**', 'foo', <type 'list'>]
sqlize
li before == [[], '**', '**', 'foo', [], []]
[18734936, 18578968, 18578968, 13208848, 18734936, 18734936]
id(previous) == 18734656
id(iterable[0])== 18734936
li after == ['**', 'foo', []]
sqlize
li before == [[], '**', '**', 'foo', [], []]
[18734696, 18578968, 18578968, 13208848, 18735016, 18734816]
id(previous) == 18734656
id(iterable[0])== 18734696
li after == ['**', 'foo', []]
Есть ли какой-либо другой объект, кроме object в Python, который имеет эту особенность?
Джон Мачин, почему вы выбрали экземпляр object в качестве часового в своей функции-генераторе? Вы уже знали об этой особенности?
person
eyquem
schedule
27.03.2011