Python:比较两个不同类型的迭代元素
Python: comparing two iterables element wise with different type
我想按元素比较两个排序列表并以不同方式处理每种情况:
- 如果两个可迭代对象都包含 an 元素,我想调用
update_func
.
- 如果只有左迭代器包含一个元素,我想调用
left_surplus_func
。
- 如果只有正确的迭代包含一个元素,我想调用
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]
我想我只需要这三个列表:creates
、updates
和 deletes
。
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
值进行操作。
我想按元素比较两个排序列表并以不同方式处理每种情况:
- 如果两个可迭代对象都包含 an 元素,我想调用
update_func
. - 如果只有左迭代器包含一个元素,我想调用
left_surplus_func
。 - 如果只有正确的迭代包含一个元素,我想调用
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]
我想我只需要这三个列表:creates
、updates
和 deletes
。
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
值进行操作。