Удалить определенные последовательные дубликаты в списке

У меня есть список таких строк:

['**', 'foo', '*', 'bar', 'bar', '**', '**', 'baz']

Я хочу заменить '**', '**' одним '**', но оставить 'bar', 'bar' нетронутым. т.е. замените любое последовательное число '**' одним. Мой текущий код выглядит так:

p = ['**', 'foo', '*', 'bar', 'bar', '**', '**', 'baz']
np = [p[0]]
for pi in range(1,len(p)):
  if p[pi] == '**' and np[-1] == '**':
    continue
  np.append(p[pi])

Есть ли еще питонический способ сделать это?


person user408952    schedule 26.03.2011    source источник
comment
Что должно произойти, если подряд три **s? Должны ли они сократиться до двух ** или одного?   -  person Daniel Stutzbach    schedule 26.03.2011
comment
@ Даниэль: т.е. замените любое последовательное число '**' одним.   -  person senderle    schedule 26.03.2011


Ответы (6)


Не уверен насчет pythonic, но это должно работать и более кратко:

star_list = ['**', 'foo', '*', 'bar', 'bar', '**', '**', 'baz']
star_list = [i for i, next_i in zip(star_list, star_list[1:] + [None]) 
             if (i, next_i) != ('**', '**')]

Вышеприведенный список копируется дважды; если вы хотите избежать этого, рассмотрите метод Тома Зича. Или вы можете сделать следующее:

from itertools import islice, izip, chain

star_list = ['**', 'foo', '*', 'bar', 'bar', '**', '**', 'baz']
sl_shift = chain(islice(star_list, 1, None), [None])
star_list = [i for i, next_i in izip(star_list, sl_shift) 
             if (i, next_i) != ('**', '**')]

Это можно обобщить и сделать удобным для итератора — не говоря уже о более читабельном — с помощью вариации рецепта pairwise из документации itertools:

from itertools import islice, izip, chain, tee
def compress(seq, x):
    seq, shift = tee(seq)
    shift = chain(islice(shift, 1, None), (object(),))
    return (i for i, j in izip(seq, shift) if (i, j) != (x, x))

Протестировано:

>>> list(compress(star_list, '**'))
['**', 'foo', '*', 'bar', 'bar', '**', 'baz']
person senderle    schedule 26.03.2011
comment
@eyquem, я нахожу это вполне читаемым как есть, но его было бы легко разбить на более мелкие части; просто определите zip_list = zip(star_list, star_list[1:] + [None]) и используйте zip_list в понимании. - person senderle; 27.03.2011
comment
@ user408952, @senderle: Все ответы более питонические, чем оригинал. Некоторые даже работают. Большинство из них гораздо более питонические, чем этот ответ. Пожалуйста, обоснуйте, почему этот ответ является приемлемым. - person John Machin; 28.03.2011

по моему это питонизм

result = [v for i, v in enumerate(L) if L[i:i+2] != ["**", "**"]]

единственная используемая «хитрость» заключается в том, что L[i:i+2] является списком из одного элемента, когда i == len(L)-1.

Обратите внимание, что то же самое выражение, конечно же, можно использовать в качестве генератора.

person 6502    schedule 27.03.2011
comment
Было бы немного питонически, если бы вы использовали enumerate: [v for i, v in enumerate(L) if L[i:i+2] != ['**', '**']] - person John Machin; 28.03.2011

Это работает. Не уверен, насколько Pythonic это.

import itertools

p = ['**', 'foo', '*', 'bar', 'bar', '**', '**', 'baz']

q = []
for key, iter in itertools.groupby(p):
    q.extend([key] * (1 if key == '**' else len(list(iter))))

print(q)
person Tom Zych    schedule 26.03.2011
comment
+1, использование groupby - хорошая альтернатива, но не могли бы вы просто сделать q.extend([key] if key == '**' else list(iter))? - person senderle; 27.03.2011
comment
Д'о, хорошая мысль. Еще не слишком хорошо знакомый с itertools, я адаптировал рецепт, который нашел. Спасибо! - person Tom Zych; 27.03.2011
comment
@eyquem, я бы посоветовал Тому Зыку отредактировать свой ответ, но я думаю, что общая идея - лучшее решение на основе групп, как, кажется, и вы, поскольку вы в основном дублируете его в своем ответе. - person senderle; 27.03.2011

Обобщенное «питоновское» решение, которое работает с любым итерируемым объектом (без резервного копирования, без копирования, без индексации, без нарезки, без сбоев, если итерируемый объект пуст) и с любой вещью для сжатия (< strong>включая None):

>>> test = ['**', 'foo', '*', 'bar', 'bar', '**', '**', '**', 'baz', '**', '**',
...      'foo', '*','*', 'bar', 'bar','bar', '**', '**','foo','bar',]
>>>
>>> def squeeze(iterable, victim, _dummy=object()):
...     previous = _dummy
...     for item in iterable:
...         if item == victim == previous: continue
...         previous = item
...         yield item
...
>>> print test
['**', 'foo', '*', 'bar', 'bar', '**', '**', '**', 'baz', '**', '**', 'foo', '*'
, '*', 'bar', 'bar', 'bar', '**', '**', 'foo', 'bar']
>>> print list(squeeze(test, "**"))
['**', 'foo', '*', 'bar', 'bar', '**', 'baz', '**', 'foo', '*', '*', 'bar', 'bar
', 'bar', '**', 'foo', 'bar']
>>> print list(squeeze(["**"], "**"))
['**']
>>> print list(squeeze(["**", "**"], "**"))
['**']
>>> print list(squeeze([], "**"))
[]
>>>

Обновление в назидание @eyquem, который заявил, что victim не может быть последовательностью (или, предположительно, набором).

Наличие контейнера жертв означает, что есть две возможные семантики:

>>> def squeeze2(iterable, victims, _dummy=object()):
...     previous = _dummy
...     for item in iterable:
...         if item == previous in victims: continue
...         previous = item
...         yield item
...
>>> def squeeze3(iterable, victims, _dummy=object()):
...     previous = _dummy
...     for item in iterable:
...         if item in victims and previous in victims: continue
...         previous = item
...         yield item
...
>>> guff = "c...d..e.f,,,g,,h,i.,.,.,.j"
>>> print "".join(squeeze2(guff, ".,"))
c.d.e.f,g,h,i.,.,.,.j
>>> print "".join(squeeze3(guff, ".,"))
c.d.e.f,g,h,i.j
>>>
person John Machin    schedule 27.03.2011
comment
@John Machin Мне нравится использовать continue : когда item == victim == previous имеет значение True , инструкция previous=item не выполняется, в отличие от того, что происходит в моем коде. Однако есть недостаток: victim не может быть последовательностью, как в ответе dugres - person eyquem; 28.03.2011
comment
@John Machin Не могли бы вы объяснить, почему вы не определяете previous = object() напрямую в коде функции squeeze() и предпочитаете определять параметр _dummy с аргументом по умолчанию < б>объект() . Кстати, что такое object()? Является ли object суперметаклассом, который используется для определения классов нового стиля? - person eyquem; 29.03.2011
comment
@John Machin С инструкцией if item == victim == previous: объект жертва не может быть ('juju','10$',513) . Аргумент, передаваемый параметру iterable, может быть чем угодно, но не аргументом, передаваемым параметру victim, вот что я имею в виду. - person eyquem; 29.03.2011
comment
@John Machin Почему вы вводите squeeze3()? - Введение squeeze2() означает, что вы согласны с тем, что squeeze() не может принимать итерацию в качестве аргумента для параметра victim, не так ли? ? Кстати, я не знал написания if item == previous in victims , Python в порядке! - person eyquem; 29.03.2011
comment
@eyquem: (1) оценка object() при определении времени сохраняет вызов функции каждый раз, когда вызывается сжатие () (2) что еще может быть object? (3) что вы имеете в виду, часто трудно определить. Я имел в виду, что сжатие легко изменить, чтобы вместить контейнер victims. (4) сжатие3(), как я объяснил (так я думал), позволяет по-другому понять, что делать с несколькими жертвами: см. вывод примера. Кстати, ответ @dugres нуждался в итерации для keep, потому что его перевернутый ответ предполагал, что вызывающий абонент будет знать всех не-жертв (вряд ли это то, что имел в виду ОП). - person John Machin; 30.03.2011
comment
@John Machin (1) Мой вопрос был совершенно глупым, потому что я знал это; но меня возмутил тот факт, что вы называете object, я не понял, почему, и (2) именно поэтому я также спросил об этом, чтобы избежать путаницы, даже если я был очень убежден, что это ЕСТЬ суперметакласс (3) хорошо (4) я согласен с вами, указание элементов, которые нельзя трогать, кажется мне противоположным обычным случаям, в которых желателен такой процесс дедупликации, и, по крайней мере, наоборот того, что спросил ОП. - person eyquem; 30.03.2011
comment
@John Machin Только что проголосовал. Спасибо за ваше объяснение - person eyquem; 08.04.2011
comment
@John Machin: извините, я нуб, но почему бы не инициализировать previous в None? - person Mat M; 17.11.2013

Решение без 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
comment
@eyquem: НЕ хороший генератор: в качестве входных данных требуется последовательность; терпит неудачу на пустой последовательности; копий (A[1:]) - person John Machin; 28.03.2011
comment
@John Machin Я полагаю, вы имеете в виду, что это генератор, но не очень хороший. Я отредактировал, чтобы учесть ваше приятное замечание. - person eyquem; 28.03.2011
comment
@eyquem: (1) Все, что hasattr пытается определить, является ли «жертва» соло или легионом, ужасно непитоновское. Просто скажи, что это контейнер, и покончим с этим. (2) Мне грустно, что вам не нравится идея о том, что «фальшивка» не может сравниться ни с чем другим объектом (лучше описываемым как «страж») — дисс, вы диссируете Кнута. (3) Кажется, вам нравится часть моего кода, и я рассказал вам о нескольких интересных вещах; как насчет плюса? - person John Machin; 30.03.2011
comment
@John Machin (1) Почему не питонический? Я добавил проверку аргумента, определяющего элементы для дедупликации, потому что столкнулся с ошибкой с iterable = ['**','**','sun','*','*','*'] и to_compress = '**' , в результате чего получилось ['**', 'sun', '*'] вместо ['**', 'sun', '*', '*', '*']. - person eyquem; 30.03.2011
comment
@John Machin Пифоническое значение этих предварительных строк с hasattr() заключается в том, что iterable и to_compress должны иметь одинаковую природу: 'sequence- последовательность" или "строка-строка", но не "последовательность-строка" (случай "строка-последовательность" не рассматривается: это должна быть проверка того, что каждый элемент to_compress имеет длину 1 и символы... я не делал' не поленюсь написать) - person eyquem; 30.03.2011
comment
@John Machin (3) Да, когда-нибудь я проголосую за, я думаю, вы заслуживаете этого гораздо больше, чем другие ответы. Но вы знаете, что: программисты ленивы.... - person eyquem; 30.03.2011
comment
@John Machin относительно (2), я хочу написать что-то как EDIT, потому что комментарий - это слишком узкое место для написания. Между прочим, Кнут называет значение, помещенное таким образом в конце данных, фиктивным значением, а не дозорным. (en.wikipedia.org/wiki/Sentinel_value) - person eyquem; 30.03.2011
comment
@John Machin Я хочу, чтобы вы ответили на мои вопросы, особенно на этот: почему вы использовали object() в качестве аргумента по умолчанию? Это потому, что любой object() является уникальным объектом? Это что-то общеизвестное? Есть другая причина? Я ждал ответа, прежде чем проголосовать за ваш ответ. - person eyquem; 07.04.2011
comment
@eyquem: экземпляр любого типа или класса, который не реализует __eq__, никогда не может сравниться с любым другим объектом. Класс object соответствует требованиям и избавляет от необходимости определять для этой цели фиктивный класс. Использование его в качестве часового, вероятно, малоизвестно; Я видел код, который использует None, что не очень хорошая идея, если None также является допустимым элементом данных (например, при использовании для представления SQL NULL). - person John Machin; 07.04.2011

person    schedule
comment
+1 за ясность. Жаль, что вы не использовали тест, если k=='**' вместо бесполезного набора - person eyquem; 27.03.2011
comment
@eyquem: использование набора делает решение расширяемым / поддерживаемым, вы можете легко сказать, что вы хотите сохранить или что вы хотите сжать. - person dugres; 28.03.2011