@@ -54,10 +54,36 @@ def override(v):
5454
5555
5656class OrderedSet (MutableSet [T ], Sequence [T ]):
57- """A Set of elements, which preserves insertion order.
57+ """
58+ A [`MutableSet`] that preserves insertion order and is also a [`Sequence`].
5859
59- This type cannot implement `MutableSequence` because OrderedSet.append
60- ignores duplicate elements and returns a `bool` instead of `None`."""
60+ [`MutableSet`]: https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableSet
61+ [`Sequence`]: https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence
62+
63+ ## Conveniences
64+ Calling `OrderedSet.append` returns `True` if the element was successfully added,
65+ and `False` if the element is a duplicate.
66+
67+ Calling `str(OrderedSet([x, y]))` is equivalent to `f"{x!r, y!r}"`.
68+ This is much prettier than using `repr`,
69+ which is expected to roundtrip through `eval`.
70+
71+ ## Gotchas
72+ This type does not implement [`MutableSequence`]
73+ because `OrderedSet.append` ignores duplicate elements
74+ and returns `bool` instead of `None`.
75+
76+ [`MutableSequence`]: https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableSequence
77+
78+ Pickling is currently not supported.
79+
80+ ### Thread Safety
81+ This type is *NOT* thread safe.
82+ Concurrent mutation will result in unexpected behavior unless you use a lock.
83+
84+ Concurrent reads are fully supported,
85+ as long as no modifications are being made.
86+ """
6187
6288 __slots__ = ("_unique" , "_elements" )
6389
@@ -66,7 +92,12 @@ class OrderedSet(MutableSet[T], Sequence[T]):
6692
6793 @override
6894 def __init__ (self , source : Iterable [T ] | None = None , / ) -> None :
69- """Create an ordered set containing the specified elements"""
95+ """
96+ Create an `OrderedSet` containing the specified elements.
97+
98+ This preserves the order of the original input
99+ and implicitly ignores duplicates.
100+ """
70101 self ._unique = set ()
71102 self ._elements = []
72103 if source is None :
@@ -83,10 +114,15 @@ def __init__(self, source: Iterable[T] | None = None, /) -> None:
83114 assert len (self ._unique ) == len (self ._elements )
84115
85116 def append (self , value : T , / ) -> bool :
86- """Append a value to the set if it doesn't already exist.
117+ """
118+ Append a value to the set if it doesn't already exist.
87119
88120 Returns `True` if successfully added, or `False` if already exists.
89- Note that the return value doesn't match list.append, which always returns `None`"""
121+
122+ There are two important differences between this method and `list.append`:
123+ 1. This method does nothing if the value is a duplicate
124+ 2. This method returns a `bool` instead of `None`
125+ """
90126 is_new = value not in self ._unique
91127 if is_new :
92128 self ._unique .add (value )
@@ -98,7 +134,7 @@ def extend(self, values: Iterable[T], /) -> bool:
98134 """
99135 Add all the specified values to the set.
100136
101- Returns True if at least one element was added,
137+ Returns ` True` if at least one element was added,
102138 or `False` if every element is a duplicate.
103139
104140 Roughly equivalent to `any(oset.append(v) for v in values)`.
@@ -110,26 +146,47 @@ def extend(self, values: Iterable[T], /) -> bool:
110146
111147 @override
112148 def add (self , value : T , / ) -> None :
113- """Add a value to the set if it doesn't already exist.
149+ """
150+ Add a value to the set if it doesn't already exist.
114151
115152 Return value is `None` for consistency with `set.add`.
116- Use `OrderedSet.append` if you want to know if the element already existed."""
153+ Use `OrderedSet.append` if you want to know if the element already existed.
154+ """
117155 self .append (value )
118156
119157 @override
120158 def discard (self , value : T , / ) -> None :
121- """Remove the element from the set if it exists."""
159+ """
160+ Remove an element from the set if it exists.
161+
162+ Unlike `OrderedSet.remove`, this method does not raise
163+ an exception if this element is missing.
164+ """
122165 if value in self ._unique :
123166 self ._elements .remove (value )
124167 self ._unique .remove (value )
125168
126169 def update (self , values : Iterable [T ], / ) -> None :
127- """Add all the"""
170+ """
171+ Add all the specified values to this set.
172+
173+ Equivalent to running
174+ ```
175+ for val in values:
176+ oset.append(val)
177+ ```
178+ """
128179 self .extend (values )
129180
130181 @override
131182 def pop (self , index : int = - 1 ) -> T :
132- """Pop an item from the end of the list (or at `index`)"""
183+ """
184+ Remove and return an item from the end of the list (or from `self[index]`).
185+
186+ Raises `IndexError` if the list is empty or `index` is out of bounds.
187+
188+ Equivalent to `list.pop`.
189+ """
133190 item = self ._elements .pop (index )
134191 self ._unique .remove (item )
135192 return item
@@ -209,15 +266,19 @@ def __ge__(self, other: object) -> bool:
209266 def sort (
210267 self , key : Optional [Callable [[T ], U ]] = None , reverse : bool = False
211268 ) -> None :
212- """Sort the elements in the set, as if calling list.sort"""
269+ """Sort the elements of the set in-place , as if calling ` list.sort`. """
213270 self ._elements .sort (key = key , reverse = reverse )
214271
215272 def reverse (self ) -> None :
216- """Reverse the elements in the set, as if calling list.reverse"""
273+ """Reverse the elements of the set in-place , as if calling ` list.reverse`. """
217274 self ._elements .reverse ()
218275
219276 def copy (self ) -> OrderedSet [T ]:
220- """Create a copy of the set"""
277+ """
278+ Create a shallow copy of the set.
279+
280+ Equivalent to `OrderedSet(self)`
281+ """
221282 return OrderedSet (self )
222283
223284 @override
@@ -261,12 +322,20 @@ def __get_pydantic_core_schema__(
261322
262323 @classmethod
263324 def dedup (self , source : Iterable [T ], / ) -> Generator [T ]:
264- """A utility method to deduplicate the specified iterable,
265- while preserving the original order.
325+ """
326+ Yield unique elements, preserving order.
266327
267- This is a generator, so does not need to wait for the entire input,
328+ This is a an iterator combinator (generator) similar to those in [`itertools`].
329+ It does not need to wait for the entire input,
268330 and will return items as soon as they are available.
269331
332+ [`itertools`]: https://docs.python.org/3/library/itertools.html
333+
334+ This is similar to [`more_itertools.unique_everseen`],
335+ although it uses an `OrderedSet` internally and does not support the `key` argument.
336+
337+ [`more_itertools.unique_everseen`]: https://more-itertools.readthedocs.io/en/v10.7.0/api.html#more_itertools.unique_everseen
338+
270339 Since: v0.1.4
271340 """
272341 oset : OrderedSet [T ] = OrderedSet ()
@@ -277,14 +346,23 @@ def dedup(self, source: Iterable[T], /) -> Generator[T]:
277346
278347 @classmethod
279348 async def dedup_async (self , source : AsyncIterable [T ], / ) -> AsyncGenerator [T ]:
280- """A utility method to deduplicate the specified iterable,
281- while preserving the original order.
349+ """
350+ Yield unique elements, preserving order.
282351
283- This is a generator, so does not need to wait for the entire input.
352+ This is a an iterator combinator (generator) similar to those in [`itertools`].
353+ It does not need to wait for the entire input,
354+ and will return items as soon as they are available.
284355 Because it is asynchronous, it does not block the thread while waiting.
285356
286357 This is an asynchronous version of `OrderedSet.dedup`.
287358
359+ It is similar to [`more_itertools.unique_everseen`],
360+ but is asynchronous, uses an `OrderedSet` internally,
361+ and does not support the `key` argument.
362+
363+ [`itertools`]: https://docs.python.org/3/library/itertools.html
364+ [`more_itertools.unique_everseen`]: https://more-itertools.readthedocs.io/en/v10.7.0/api.html#more_itertools.unique_everseen
365+
288366 Since: v0.1.4
289367 """
290368 # Defined in PEP 525
0 commit comments