Python:比较两个不同类型的迭代元素

Python: comparing two iterables element wise with different type

我想按元素比较两个排序列表并以不同方式处理每种情况:

  1. 如果两个可迭代对象都包含 an 元素,我想调用 update_func.
  2. 如果只有左迭代器包含一个元素,我想调用left_surplus_func
  3. 如果只有正确的迭代包含一个元素,我想调用right_surplus_func

不幸的是,zip 在这种情况下对我没有帮助,因为它会创建不相关元素的元组。此外,就我而言,列表或可迭代对象都是不同的且不可转换的类型。

这与 How can I compare two lists in python and return matches and Checking if any elements in one list are in another 相似,但还不够接近,无法成为真正的解决方案。

我想出了一个可行的解决方案(除了两个可迭代对象不能包含 None):

def compare_iterables_elemet_wise(left, right, compare_func,
                          left_surplus_func, update_func, right_surplus_func):
    """
    :type left: collections.Iterable[U]
    :type right: collections.Iterable[T]
    :type compare_func: (U, T) -> int
    :type left_surplus_func: (U) -> None
    :type update_func: (U, T) -> None
    :type right_surplus_func: (T) -> None
    """
    while True:
        try:
            l = next(left)
        except StopIteration:
            l = None  # Evil hack, but acceptable for this example
        try:
            r = next(right)
        except StopIteration:
            r = None

        if l is None and r is not None:
            cmp_res = 1
        elif l is not None and r is None:
            cmp_res = -1
        elif l is None and r is None:
            return
        else:
            cmp_res = compare_func(l, r)

        if cmp_res == 0:
            update_func(l, r)
        elif cmp_res < 0:
            left_surplus_func(l)
            right = itertools.chain([r], right)  # aka right.unget(r)
        else:
            right_surplus_func(r)
            left = itertools.chain([l], left)  # aka left.unget(l)

是否有更 pythonic 的方法来存档类似的结果?我对我的解决方案有点不满意,因为它取决于函数的外部副作用。有一个纯功能解决方案会很好。

编辑:这是我的测试用例:

creates = []
updates = []
deletes = []

def compare(a, obj):
    return cmp(int(a), obj)

def handle_left(a):
    creates.append(a)

def update(a, obj):
    updates.append((a, obj))

def handle_right(obj):
    deletes.append(obj)

left = list('12356')
right = [1, 3, 4, 6, 7]
compare_iterables_elemet_wise(iter(left), iter(right), compare, handle_left, update, handle_right)

assert creates == ['2', '5']
assert updates == [('1', 1), ('3', 3), ('6', 6)]
assert deletes == [4, 7]

我想我只需要这三个列表:createsupdatesdeletes

Edit2:设置操作:

这和我的问题类似,只是左右的类型不同:

left = [1, 2, 3, 5, 6]

right = [1, 3, 4, 6, 7]

In [10]: set(left) - set(right)
Out[10]: {2, 5}

In [11]: set(right) - set(left)
Out[11]: {4, 7}


In [14]: set(right).intersection(set(left))
Out[14]: {1, 3, 6}

您可以使用 next 内置函数的默认参数来简化 while 循环开头的 try 语句。

def compare_iterables_element_wise(left, right, compare_func,
                      left_surplus_func, update_func, right_surplus_func):

    l, r = next(left, None), next(right, None)
    while l or r:
        if l is None and r is not None:
            cmp_res = 1
        elif l is not None and r is None:
            cmp_res = -1
        else:
            cmp_res = compare_func(l, r)

        if cmp_res == 0:
            update_func(l, r)
            l, r = next(left, None), next(right, None)
        elif cmp_res < 0:
            left_surplus_func(l)
            l = next(left, None)
        else:
            right_surplus_func(r)
            r = next(right, None)

我还建议 compare_func 处理其中一个参数是 None 的情况,以便您摆脱第一个 if.

从您的第二次编辑来看,使用集合似乎是个好主意,前提是您可以将对象转换为通用类型。您可以使用 __hash__ 方法将整数与每个对象相关联,并根据 hash 值进行操作。