Элегантный способ сравнить последовательности - PullRequest
11 голосов
/ 23 мая 2009

Предоставляет ли python элегантный способ проверки «равенства» последовательностей разных типов? Следующие работы, но они кажутся довольно уродливыми и многословными для кода Python:

def comp1(a, b):
    if len(a) != len(b):
        return False
    for i, v in enumerate(a):
        if v != b[i]:
            return False
    return True

Следующее немного короче, но также менее эффективно, поскольку создается третья последовательность:

def comp2(a, b):
    for l, r in map(None, a, b):
        if l != r:
            return False
    return True

Включение одного из этих примеров в понимание списка не совсем то, что я ищу.

Редактировать: В идеале я ищу решение, которое не создает другую последовательность во время сравнения.

Ответы [ 8 ]

17 голосов
/ 23 мая 2009

Преобразование обеих последовательностей в списки и использование встроенного сравнения списков. Этого должно быть достаточно, если только ваши последовательности не очень большие.

list(a) == list(b)

Edit:

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

tuple(a) == tuple(b)
12 голосов
/ 23 мая 2009

Вы можете определить равенство любых двух итераций (строк, кортежей, списков, даже пользовательских последовательностей), не создавая и не сохраняя дубликаты списков, используя следующие:

all(x == y for x, y in itertools.izip_longest(a, b))

Обратите внимание, что если две итерации не имеют одинаковую длину, более короткая будет дополнена None с. Другими словами, он будет считать [1, 2, None] равным (1, 2).

Редактировать: Как отмечает Камил в комментариях, izip_longest доступна только в Python 2.6. Однако, документы для функции также предоставляют альтернативную реализацию, которая должна работать вплоть до 2.3.

Редактировать 2: После тестирования на нескольких разных машинах, похоже, что это только быстрее, чем list(a) == list(b) в определенных обстоятельствах, которые я не могу выделить. В большинстве случаев это занимает в семь раз больше времени. Тем не менее, я также обнаружил, что tuple(a) == tuple(b) постоянно, по крайней мере, в два раза быстрее, чем list версия.

5 голосов
/ 09 августа 2010

Помимо дополнительной памяти, используемой для создания временных списков / кортежей, эти ответы будут потеряны для решений короткого замыкания генератора для больших последовательностей, когда неравенство возникает в начале последовательностей

from itertools import starmap, izip
from operator import eq
all(starmap(eq, izip(x, y)))

или более кратко

from itertools import imap
from operator import eq
all(imap(eq, x, y))

некоторые тесты от ipython

x=range(1000)
y=range(1000); y[10]=0

timeit tuple(x) == tuple(y)
100000 loops, best of 3: 16.9 us per loop

timeit all(imap(eq, x, y))
100000 loops, best of 3: 2.86 us per loop
2 голосов
/ 23 мая 2009

Похоже, tuple (a) == tuple (b) - лучший общий выбор. Или, может быть, сравнение кортежей с предыдущей проверкой len, если они часто будут разной длины. Это создает дополнительные списки, но, надеюсь, не проблема, за исключением действительно огромных списков. Вот мое сравнение предложенных альтернатив:

import timeit

tests = (
'''
a=b=[5]*100
''',

'''
a=[5]*100
b=[5]*3
''',

'''
a=b=(5,)*100
''',

'''
a=b="This on is a string" * 5
''',

'''
import array
a=b=array.array('B', "This on is a string" * 5)
'''
)

common = '''import itertools
def comp1(a, b):
    if len(a) != len(b):
        return False
    for i, v in enumerate(a):
        if v != b[i]:
            return False
    return True'''

for i, setup in enumerate(tests):
    t1 = timeit.Timer("comp1(a, b)", setup + common)
    t2 = timeit.Timer("all(x == y for x, y in itertools.izip_longest(a, b))", setup + common)
    t3 = timeit.Timer("all([x == y for x, y in itertools.izip_longest(a, b)])", setup + common)
    t4 = timeit.Timer("list(a) == list(b)", setup + common)
    t5 = timeit.Timer("tuple(a) == tuple(b)", setup + common)

    print '==test %d==' % i
    print '   comp1: %g' % t1.timeit()
    print ' all gen: %g' % t2.timeit()
    print 'all list: %g' % t3.timeit()
    print '    list: %g' % t4.timeit()
    print '   tuple: %g\n' % t5.timeit()

Вот результаты:

==test 0==
   comp1: 27.8089
 all gen: 31.1406
all list: 29.4887
    list: 3.58438
   tuple: 3.25859

==test 1==
   comp1: 0.833313
 all gen: 3.8026
all list: 33.5288
    list: 1.90453
   tuple: 1.74985

==test 2==
   comp1: 30.606
 all gen: 31.4755
all list: 29.5637
    list: 3.56635
   tuple: 1.60032

==test 3==
   comp1: 33.3725
 all gen: 35.3699
all list: 34.2619
    list: 10.2443
   tuple: 10.1124

==test 4==
   comp1: 31.7014
 all gen: 32.0051
all list: 31.0664
    list: 8.35031
   tuple: 8.16301

Редактировать: Добавлено еще несколько тестов. Он работал на AMD 939 3800+ с 2 ГБ оперативной памяти. Linux 32bit, Python 2.6.2

1 голос
/ 23 мая 2009

Поскольку вы поместили слово «равенство» в кавычки, я предполагаю, что вы хотели бы знать, как списки одинаковы и как различны. Проверьте difflib , который имеет класс SequenceMatcher:

    sm = difflib.SequenceMatcher(None, a, b)
    for opcode in sm.get_opcodes():
        print "    (%s %d:%d %d:%d)" % opcode

Вы получите последовательность описаний различий. Это довольно просто превратить в diff -подобный вывод.

0 голосов
/ 10 августа 2016

Я думаю, это хорошая идея для особого случая, когда обе последовательности имеют тип list. Сравнение двух списков быстрее (и более эффективно использует память), чем преобразование обоих в кортежи.

Если a или b не являются списками, они оба преобразуются в tuple. Нет затрат, если один или оба уже являются кортежами, так как tuple() просто возвращает ссылку на исходный объект в этом случае.

def comp(a, b):
    if len(a) != len(b):
        return False
    if type(a) == type(b) == list:
        return a == b
    a = tuple(a)
    b = tuple(b)
    return a == b
0 голосов
/ 24 мая 2009

Этот «функциональный» код должен быть достаточно быстрым и универсальным для всех целей.

# python 2.6 ≤ x < 3.0
import operator, itertools as it

def seq_cmp(seqa, seqb):
    return all(it.starmap(operator.eq, it.izip_longest(seqa, seqb)))

Если в Python 2.5 используется определение для izip_longest от там .

0 голосов
/ 23 мая 2009

Возможно, это не так эффективно, но выглядит странно:

def cmpLists(a, b):
    return len(a) == len(b) and (False not in [a[i] == b[i] for i in range(0,len(a)])

Я не знаю функцию "все", которую Бен упомянул , но, возможно, вы могли бы использовать ее вместо "False not in"

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...