Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce USet and UInterval classes with withrepr decorator #59

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ufpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ def new_func(*args, **kwargs):
# GitHub package
__github_version__ = '0.1'
from ufpy.github import *

# Algebra package
__algebra_version = '0.1'
from ufpy.algebra import *
1 change: 1 addition & 0 deletions ufpy/algebra/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ufpy.algebra.sets import *
127 changes: 127 additions & 0 deletions ufpy/algebra/sets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from __future__ import annotations

from ufpy.math_op import i_generator, r_generator
from ufpy.utils import is_iterable

__all__ = (
'USet',
)

from typing import Iterable, Iterator, Optional, TypeVar, overload

T = TypeVar('T')
OT = TypeVar('OT')

@i_generator
@r_generator
class USet[T]:
U: USet = None

@overload
def __init__(self, *values: T) -> None: ...
@overload
def __init__(self, *values: T, auto_update_U: bool) -> None: ...
@overload
def __init__(self, *, iterable: Iterable[T]) -> None: ...
@overload
def __init__(self, *, iterable: Iterable[T], auto_update_U: bool) -> None: ...

def __init__(self, *values: T, iterable: Optional[Iterable[T]] = None, auto_update_U: bool = True) -> None:
if USet.U == None and auto_update_U:
USet.U = _U()
bleudev marked this conversation as resolved.
Show resolved Hide resolved

self.__set = list(sorted(set(iterable))) if iterable else list(sorted(set(values)))
self.__auto_update_U = auto_update_U
self.__update__()

@property
def set(self) -> set[T]:
return set(self.__set)

@set.setter
def set(self, value: Iterable[T]):
self.__set = list(value)
self.__update__()

# When USet updates (not required, that new USet must be not same that old one)
def __update__(self):
bleudev marked this conversation as resolved.
Show resolved Hide resolved
if self.__auto_update_U:
USet.U |= self

# Convert to other types
def __repr__(self) -> str:
return 'u{' + ', '.join([str(i) for i in self.__set]) + '}'

# Iteration
def __iter__(self) -> Iterator[T]:
return iter(self.__set)

# Or
def _or(self, other: Iterable[OT] | OT) -> USet[T | OT]:
if not is_iterable(other):
other = [other]
return USet(iterable=set(self) | set(other), auto_update_U=self.__auto_update_U)

def __or__(self, other: Iterable[OT] | OT) -> USet[T | OT]:
return self._or(other)

def __add__(self, other: Iterable[OT] | OT) -> USet[T | OT]:
return self._or(other)

# Substract
def sub(self, other: Iterable[OT] | OT) -> USet[T]:
if not is_iterable(other):
other = [other]
return USet(iterable=set(self) - set(other), auto_update_U=self.__auto_update_U)

def __sub__(self, other: Iterable[OT] | OT) -> USet[T]:
return self.sub(other)

def __rsub__(self, other: Iterable[OT] | OT) -> USet[T]:
return USet(iterable=other if is_iterable(other) else [other]).sub(self)

def __truediv__(self, other: Iterable[OT] | OT) -> USet[T]:
return self.sub(other)

# And
def _and(self, other: Iterable[OT] | OT) -> USet:
if not is_iterable(other):
other = [other]
return USet(iterable=set(self) & set(other), auto_update_U=self.__auto_update_U)

def __and__(self, other: Iterable[OT] | OT) -> USet:
return self._and(other)

def __mul__(self, other: Iterable[OT] | OT) -> USet:
return self._and(other)

# Not
def _not(self, s: Optional[USet[OT]] = None) -> USet[OT]:
return (s or USet.U) - self

def __neg__(self) -> USet:
return self._not()

# Implication
def implicate(self, other: Iterable[OT] | OT) -> USet:
if not is_iterable(other):
other = [other]
return USet(iterable=(-self + other), auto_update_U=self.__auto_update_U)

def __gt__(self, other: Iterable[OT] | OT) -> USet:
bleudev marked this conversation as resolved.
Show resolved Hide resolved
return self.implicate(other)

def __ge__(self, other: Iterable[OT] | OT) -> USet:
return self > other

def __lt__(self, other: Iterable[OT] | OT) -> USet:
other2 = USet(*(other if is_iterable(other) else [other]))
return other2.implicate(self)

def __le__(self, other: Iterable[OT] | OT) -> USet:
return self < other


class _U(USet[T]):
def __init__(self, *values: T) -> None:
super().__init__(*values, auto_update_U=False)
62 changes: 36 additions & 26 deletions ufpy/math_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,62 +8,72 @@

T = TypeVar('T')

def check(t: Type[T], s: str, astr: str):
bleudev marked this conversation as resolved.
Show resolved Hide resolved
s2 = f'__{astr}{s.replace('__', '')}__'
return s in t.__dict__ and s2 not in t.__dict__

def i_generator(t: Type[T]) -> Type[T]:
if '__add__' in t.__dict__:
def check_i(s: str):
return check(t, s, 'i')

if check_i('__add__'):
t.__iadd__ = t.__add__
if '__sub__' in t.__dict__:
if check_i('__sub__'):
t.__isub__ = t.__sub__
if '__mul__' in t.__dict__:
if check_i('__mul__'):
t.__imul__ = t.__mul__
if '__floordiv__' in t.__dict__:
if check_i('__floordiv__'):
t.__ifloordiv__ = t.__floordiv__
if '__div__' in t.__dict__:
if check_i('__div__'):
t.__idiv__ = t.__div__
if '__truediv__' in t.__dict__:
if check_i('__truediv__'):
t.__itruediv__ = t.__truediv__
if '__mod__' in t.__dict__:
if check_i('__mod__'):
t.__imod__ = t.__mod__
if '__pow__' in t.__dict__:
if check_i('__pow__'):
t.__ipow__ = t.__pow__
if '__lshift__' in t.__dict__:
if check_i('__lshift__'):
t.__ilshift__ = t.__lshift__
if '__rshift__' in t.__dict__:
if check_i('__rshift__'):
t.__irshift__ = t.__rshift__
if '__and__' in t.__dict__:
if check_i('__and__'):
t.__iand__ = t.__and__
if '__or__' in t.__dict__:
if check_i('__or__'):
t.__ior__ = t.__or__
if '__xor__' in t.__dict__:
if check_i('__xor__'):
t.__ixor__ = t.__xor__

return t

def r_generator(t: Type[T]) -> Type[T]:
if '__add__' in t.__dict__:
def check_r(s: str):
return check(t, s, 'r')

if check_r('__add__'):
t.__radd__ = t.__add__
if '__sub__' in t.__dict__:
if check_r('__sub__'):
t.__rsub__ = t.__sub__
if '__mul__' in t.__dict__:
if check_r('__mul__'):
t.__rmul__ = t.__mul__
if '__floordiv__' in t.__dict__:
if check_r('__floordiv__'):
t.__rfloordiv__ = t.__floordiv__
if '__div__' in t.__dict__:
if check_r('__div__'):
t.__rdiv__ = t.__div__
if '__truediv__' in t.__dict__:
if check_r('__truediv__'):
t.__rtruediv__ = t.__truediv__
if '__mod__' in t.__dict__:
if check_r('__mod__'):
t.__rmod__ = t.__mod__
if '__pow__' in t.__dict__:
if check_r('__pow__'):
t.__rpow__ = t.__pow__
if '__lshift__' in t.__dict__:
if check_r('__lshift__'):
t.__rlshift__ = t.__lshift__
if '__rshift__' in t.__dict__:
if check_r('__rshift__'):
t.__rrshift__ = t.__rshift__
if '__and__' in t.__dict__:
if check_r('__and__'):
t.__rand__ = t.__and__
if '__or__' in t.__dict__:
if check_r('__or__'):
t.__ror__ = t.__or__
if '__xor__' in t.__dict__:
if check_r('__xor__'):
t.__rxor__ = t.__xor__

return t
26 changes: 25 additions & 1 deletion ufpy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
'get_items_for_several_keys',
'set_items_for_several_keys',
'del_items_for_several_keys',
'withrepr'
)

from typing import TypeVar
from typing import Any, Callable, TypeVar, Iterable
import functools

from ufpy.typ import SupportsGet, SupportsSetItem, SupportsDelItem, AnyCollection

Expand All @@ -31,3 +33,25 @@ def del_items_for_several_keys(o: SupportsDelItem[KT, VT], keys: AnyCollection[K
for k in keys:
del res[k]
return res

# Useful decorators

class __reprwrapper:
def __init__(self, repr, func):
self._repr = repr
self._func = func
functools.update_wrapper(self, func)
def __call__(self, *args, **kw):
return self._func(*args, **kw)
def __repr__(self):
return self._repr(self._func)

T = TypeVar('T', bound=Callable)

def withrepr(f: Callable[[T], str]):
def _wrap(func: T):
return __reprwrapper(f, func)
return _wrap

def is_iterable(o: object) -> bool:
return isinstance(o, Iterable)
Loading