Handle unhashable element input to list_update and list_difference_update - #4975
Handle unhashable element input to list_update and list_difference_update#4975nikolajmunk wants to merge 1 commit into
list_update and list_difference_update#4975Conversation
- Restrict input types of list_update and list_difference_update to iterables of hashables. - Add instance check to avoid unnecessary conversion to list/set
|
Optimizing hashable types while still allowing unhashable types would be nice. def list_difference_update(l1: Iterable[T], l2: Iterable[T]) -> list[T]:
if isinstance(l2, Iterator):
l2 = tuple(l2)
with contextlib.suppress(TypeError):
l2 = set(l2)
return [e for e in l1 if e not in l2] |
|
Yep, that works for cases when |
Not really liking this solution: def list_difference_update(l1, l2):
if isinstance(l2, Iterator):
l2 = tuple(l2)
with contextlib.suppress(TypeError):
l2_set = set(l2)
def is_in_l2(e) -> bool:
try:
return e in l2_set
except TypeError:
return e in l2
return [e for e in l1 if not is_in_l2(e)]Here a small testfrom typing import Iterator
import contextlib
def list_difference_update(l1, l2):
if isinstance(l2, Iterator):
l2 = tuple(l2)
with contextlib.suppress(TypeError):
l2_set = set(l2)
def is_in_l2(e) -> bool:
try:
return e in l2_set
except TypeError:
return e in l2
return [e for e in l1 if not is_in_l2(e)]
class MyClass:
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
__hash__ = None
a = [MyClass("a"), MyClass("b"), MyClass("c")]
b = [1, 2, 3]
c = list_difference_update(a, b)
print(c) |
Haha yes, I have a few solutions that look similar. It's possible to do this in a nice-looking way, but all my attempts have resulted in an algorithm that is markedly slower when all input is hashable, which it will be the vast majority of the time. Catching the edge case is simply very expensive. |
Overview: What does this pull request change?
This addresses a small regression introduced by #4939, where an unhashable element in
l1and/orl2would throw aTypeError. This PR changes the input types oflist_updateandlist_difference_updatetol1: Iterable[H], l2: Iterable[J]whereHandJare Hashable TypeVars.I have marked this PR as a draft because it is not 100% clear to me that disallowing unhashable input is the right thing to do. We never need to operate on unhashable input in the library, but perhaps a user has some weird use for it somewhere.
There are numerous easy ways to handle both hashable and unhashable input without requiring some kind of
has_unhashable: boolflag, but all of them are going to be slower than the current implementation. I'm happy to write such an implementation or take suggestions for one, if people feel like we need it that is. Otherwise this should be good enough (though maybe I should write an overload or two for nicer type hints)!Looking forward to any feedback.
Reviewer Checklist