Created
April 11, 2023 17:26
-
-
Save qexat/656d4fad3da86e784d2c5c229ec721b9 to your computer and use it in GitHub Desktop.
is this rust? is this python? the answer is: yes!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections.abc import Callable, Iterable, Iterator | |
from typing import Generic, Never, Self, TypeVar, overload | |
T = TypeVar("T") | |
class PrivacyError(TypeError): | |
@classmethod | |
def from_private_constructor(cls, type: type, /, *public_constructors: str) -> Self: | |
""" | |
Constructor for PrivacyError. | |
Used when some constructor C of the type `type` is not public. | |
""" | |
tname = type.__name__ | |
err = cls(f"{tname!r} cannot be instantiated directly") | |
if public_constructors: | |
err.add_note("=> \x1b[1mTry using:\x1b[22m") | |
for name in public_constructors: | |
err.add_note(f" • \x1b[33m{tname}\x1b[39m.\x1b[34m{name}\x1b[39m()") | |
return err | |
class Vec(Generic[T]): | |
def __new__(cls) -> Never: | |
raise PrivacyError.from_private_constructor(cls, "new", "from_iter") | |
def __init__(self) -> Never: | |
self.__internal: list[T] # so static type checkers won't complain | |
raise PrivacyError.from_private_constructor(type(self), "new", "from_iter") | |
@classmethod | |
def __private_new(cls) -> Self: | |
return super().__new__(cls) | |
def __private_init(self, iterable: Iterable[T] | None = None, /) -> None: | |
self.__internal = [] if iterable is None else list(iterable) | |
def __repr__(self) -> str: | |
return repr(self.__internal) | |
def __bool__(self) -> bool: | |
return bool(self.__internal) | |
def __len__(self) -> int: | |
return len(self.__internal) | |
@overload | |
def __getitem__(self, index_or_slice: int) -> T: | |
pass | |
@overload | |
def __getitem__(self, index_or_slice: slice) -> list[T]: | |
pass | |
def __getitem__(self, index_or_slice: int | slice) -> T | list[T]: | |
if isinstance(index_or_slice, int) and not self.__is_in_bounds(index_or_slice): | |
raise IndexError(f"index {index_or_slice} is out of bound") | |
return self.__internal[index_or_slice] | |
@overload | |
def __setitem__(self, index_or_slice: int, item_or_part: T) -> None: | |
pass | |
@overload | |
def __setitem__(self, index_or_slice: slice, item_or_part: Iterable[T]) -> None: | |
pass | |
def __setitem__( | |
self, | |
index_or_slice: int | slice, | |
item_or_part: T | Iterable[T], | |
) -> None: | |
self.__internal[index_or_slice] = item_or_part # type: ignore | |
def __iter__(self) -> Iterator[T]: | |
yield from self.__internal | |
@classmethod | |
def new(cls) -> Self: | |
""" | |
Construct a new empty Vec. | |
""" | |
self = cls.__private_new() | |
self.__private_init() | |
return self | |
@classmethod | |
def from_iter(cls, iterable: Iterable[T], /) -> Self: | |
""" | |
Construct a new Vec filled with the elements from the provided `iterable`. | |
""" | |
self = cls.__private_new() | |
self.__private_init(iterable) | |
return self | |
def __is_in_bounds(self, index: int) -> bool: | |
""" | |
Private helper. Check if `index` would be valid in the current Vec. | |
""" | |
return 0 <= index < len(self) | |
def push(self, item: T) -> None: | |
""" | |
Add the `item` at the end of the Vec. | |
""" | |
self.__internal.append(item) | |
def append(self, other: "Vec[T]") -> None: | |
""" | |
Append to this Vec another one, leaving this latter empty. | |
""" | |
self.extend(other) | |
other.clear() | |
def extend(self, other: Iterable[T]) -> None: | |
""" | |
Extend the Vec with the elements of the `other` iterable. | |
""" | |
self.__internal.extend(other) | |
def insert(self, index: int, item: T) -> None: | |
""" | |
Insert in the Vec at the given `index` the provided `item`. | |
""" | |
if index > len(self): | |
raise IndexError(f"index {index} is out of bounds") | |
self.__internal.insert(index, item) | |
def pop(self) -> T | None: | |
""" | |
Remove and return the last element of the Vec if it is not empty, else None. | |
""" | |
return None if self.is_empty() else self.__internal.pop() | |
def __pop(self) -> T: | |
""" | |
Internal helper. Same as `Vec.pop()` but does not check for emptiness. | |
Might raise an error if not handled. | |
""" | |
return self.__internal.pop() | |
def remove(self, index: int) -> T: | |
""" | |
Remove from the Vec the item at the given `index`. | |
""" | |
if not self.__is_in_bounds(index): | |
raise IndexError(f"index {index} is out of bounds") | |
return self.__internal.pop(index) | |
def truncate(self, length: int) -> None: | |
""" | |
Truncate the Vec to a provided `length`. | |
If this latter is greater or equal than the current length, nothing is done. | |
""" | |
if length >= len(self): | |
return | |
for _ in range(len(self) - length): | |
self.pop() | |
def clear(self) -> None: | |
""" | |
Clear the Vec by removing all its elements. | |
""" | |
self.__internal.clear() | |
def split_off(self, at: int) -> Self: | |
""" | |
Split the Vec at the given index in two. | |
The original is truncated from 0 to `at`, the returned is the items from `at` to the end. | |
""" | |
if not self.__is_in_bounds(at): | |
raise IndexError(f"index {at} is out of bounds") | |
trailing = list(self.__pop() for _ in range(len(self) - at)) | |
return type(self).from_iter(reversed(trailing)) | |
def resize_with(self, new_length: int, filler: Callable[[], T]) -> None: | |
""" | |
Resize the Vec to a given `new_length`, with the new items generated by calling the provided `filler`. | |
If the `new_length` equals the current length, nothing is done. If it is smaller, the Vec is truncated. | |
""" | |
current_len = len(self) | |
if current_len >= new_length: | |
self.truncate(new_length) | |
return | |
for _ in range(new_length - current_len): | |
self.push(filler()) | |
def retain(self, predicate: Callable[[T], bool]) -> None: | |
""" | |
Remove the items from the Vec if they do not satisfy the given `predicate` (i.e. `predicate(item)` is `False`). | |
This is similar to `filter`, but in place. | |
""" | |
for index, item in enumerate(self): | |
if not predicate(item): | |
self.remove(index) | |
def dedup(self) -> None: | |
""" | |
Remove any item of the Vec if it is encountered more than once. | |
""" | |
self.retain(lambda v: self.__internal.count(v) == 1) | |
def splice(self, slice: slice, replace_with: T) -> list[T]: | |
""" | |
Replace the items of the Vec in the given `slice` by `replace_with`. | |
Return the items that got replaced. | |
""" | |
replaced_part = self[slice] | |
for index, _ in enumerate(self[slice]): | |
self[index] = replace_with | |
return replaced_part | |
def is_empty(self) -> bool: | |
""" | |
Return whenever the Vec is empty or not. | |
""" | |
return len(self) == 0 | |
def count(self, value: T) -> int: | |
""" | |
Count the occurrences of `value` in the Vec. | |
""" | |
return self.__internal.count(value) | |
class __VecMacro(type): | |
def __getitem__(self, items: T | tuple[T, ...]) -> Vec[T]: | |
return Vec.from_iter(items if isinstance(items, tuple) else (items,)) # type: ignore | |
class vecǃ(metaclass=__VecMacro): | |
""" | |
Macro to construct a `Vec` object easily. | |
""" | |
def main() -> None: | |
v = vecǃ[3, 5, 1, 4] | |
w = Vec.from_iter([9, 8, 2, 7]) | |
print(v, w) | |
v.append(w) | |
print(v, w) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment