Skip to content

Instantly share code, notes, and snippets.

@qexat
Created April 11, 2023 17:26
Show Gist options
  • Save qexat/656d4fad3da86e784d2c5c229ec721b9 to your computer and use it in GitHub Desktop.
Save qexat/656d4fad3da86e784d2c5c229ec721b9 to your computer and use it in GitHub Desktop.
is this rust? is this python? the answer is: yes!
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