Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 pycommons/lang/tuple/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .pair import Pair, ImmutablePair, MutablePair
from .triple import Triple, ImmutableTriple, MutableTriple

__all__ = ["Pair", "ImmutablePair", "MutablePair", "Triple", "ImmutableTriple", "MutableTriple"]
91 changes: 91 additions & 0 deletions pycommons/lang/tuple/pair.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from __future__ import annotations

import typing
from abc import ABC, abstractmethod
from collections import UserList
from typing import TypeVar, Generic, Union, Any

_L = TypeVar("_L")
_R = TypeVar("_R")


class Pair(ABC, Generic[_L, _R]):
@classmethod
def of(cls, left: _L, right: _R) -> ImmutablePair[_L, _R]:
return ImmutablePair(left, right)

@property
def left(self) -> _L:
return typing.cast(_L, self[0])

@property
def right(self) -> _R:
return typing.cast(_R, self[1])

@property
def key(self) -> _L:
return self.left

@property
def value(self) -> _R:
return self.right

def get_left(self) -> _L:
return self.left

def get_right(self) -> _R:
return self.right

def get_key(self) -> _L:
return self.left

def get_value(self) -> _R:
return self.right

@abstractmethod
def __getitem__(self, item: Any) -> Any:
... # pragma: no cover

def __str__(self) -> str:
return self.to_string("({0}, {1})")

def to_string(self, fmt: str) -> str:
return fmt.format(self.left, self.right)


class ImmutablePair(tuple, Pair[_L, _R], Generic[_L, _R]): # type: ignore
def __getitem__(self, item: Any) -> Any:
return typing.cast(Union[_L, _R], tuple.__getitem__(self, item))

def __new__(cls, left: _L, right: _R) -> ImmutablePair[_L, _R]:
return super().__new__(cls, [left, right]) # type: ignore


class MutablePair(UserList, Pair, Generic[_L, _R]): # type: ignore
def __getitem__(self, item: Any) -> Any:
return typing.cast(Union[_L, _R], UserList.__getitem__(self, item))

def __init__(self, left: _L, right: _R) -> None:
super().__init__([left, right])

@property
def left(self) -> _L:
return typing.cast(_L, super().left)

@left.setter
def left(self, left: _L) -> None:
self[0] = left

@property
def right(self) -> _R:
return typing.cast(_R, super().right)

@right.setter
def right(self, right: _R) -> None:
self[1] = right

def set_left(self, key: _L) -> None:
self.left = key

def set_right(self, value: _R) -> None:
self.right = value
96 changes: 96 additions & 0 deletions pycommons/lang/tuple/triple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from __future__ import annotations

import typing
from abc import ABC, abstractmethod
from collections import UserList
from typing import TypeVar, Generic, Union, Tuple, Any

_L = TypeVar("_L")
_M = TypeVar("_M")
_R = TypeVar("_R")


class Triple(ABC, Generic[_L, _M, _R]):
@classmethod
def of(cls, left: _L, middle: _M, right: _R) -> ImmutableTriple[_L, _M, _R]:
return ImmutableTriple(left, middle, right)

@property
def left(self) -> _L:
return typing.cast(_L, self[0])

@property
def middle(self) -> _M:
return typing.cast(_M, self[1])

@property
def right(self) -> _R:
return typing.cast(_R, self[2])

def get_left(self) -> _L:
return self.left

def get_middle(self) -> _M:
return self.middle

def get_right(self) -> _R:
return self.right

@abstractmethod
def __getitem__(self, item: Any) -> Any: # pragma: no cover
...

def __str__(self) -> str:
return self.to_string("({0}, {1}, {2})")

def to_string(self, fmt: str) -> str:
return fmt.format(self.left, self.middle, self.right)


class ImmutableTriple(Tuple, Triple[_L, _M, _R], Generic[_L, _M, _R]): # type: ignore
def __getitem__(self, item: Any) -> Any:
return typing.cast(Union[_L, _M, _R], tuple.__getitem__(self, item))

def __new__(cls, left: _L, middle: _M, right: _R) -> ImmutableTriple[_L, _M, _R]:
return super().__new__(cls, [left, middle, right]) # type: ignore


class MutableTriple(UserList, Triple[_L, _M, _R], Generic[_L, _M, _R]): # type: ignore
def __getitem__(self, item: Any) -> Any:
return typing.cast(Union[_L, _M, _R], UserList.__getitem__(self, item))

def __init__(self, left: _L, middle: _M, right: _R) -> None:
super().__init__([left, middle, right])

@property
def left(self) -> _L:
return super().left

@left.setter
def left(self, left: _L) -> None:
self[0] = left

@property
def middle(self) -> _M:
return super().middle

@middle.setter
def middle(self, middle: _M) -> None:
self[1] = middle

@property
def right(self) -> _R:
return super().right

@right.setter
def right(self, right: _R) -> None:
self[2] = right

def set_left(self, left: _L) -> None:
self.left = left

def set_middle(self, middle: _M) -> None:
self.middle = middle

def set_right(self, right: _R) -> None:
self.right = right
Empty file.
47 changes: 47 additions & 0 deletions tests/pycommons/lang/tuple/test_pair.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from unittest import TestCase

from pycommons.lang.tuple import Pair, MutablePair


class TestImmutablePair(TestCase):
def test_pair_methods(self):
pair: Pair[int, int] = Pair.of(1, 6)
self.assertEqual(1, pair.left)
self.assertEqual(1, pair.get_left())
self.assertEqual(1, pair.get_key())

self.assertEqual(6, pair.right)
self.assertEqual(6, pair.get_right())
self.assertEqual(6, pair.get_value())

self.assertEqual("(1, 6)", str(pair))
self.assertEqual("[1, 6]", pair.to_string("[{0}, {1}]"))


class TestMutablePair(TestCase):
def test_pair_methods(self):
pair: MutablePair[int, int] = MutablePair(1, 6)
self.assertEqual(1, pair.left)
self.assertEqual(1, pair.get_left())
self.assertEqual(1, pair.key)
self.assertEqual(1, pair.get_key())

self.assertEqual(6, pair.right)
self.assertEqual(6, pair.get_right())
self.assertEqual(6, pair.value)
self.assertEqual(6, pair.get_value())

self.assertEqual("(1, 6)", str(pair))
self.assertEqual("[1, 6]", pair.to_string("[{0}, {1}]"))

pair.left = 2
self.assertEqual(2, pair.left)

pair.right = 7
self.assertEqual(7, pair.right)

pair.set_left(3)
self.assertEqual(3, pair.left)

pair.set_right(8)
self.assertEqual(8, pair.right)
53 changes: 53 additions & 0 deletions tests/pycommons/lang/tuple/test_triple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from unittest import TestCase

from pycommons.lang.tuple import Triple, MutableTriple


class TestImmutableTriple(TestCase):
def test_triple_methods(self):
pair: Triple[int, int] = Triple.of(1, 5, 6)
self.assertEqual(1, pair.left)
self.assertEqual(1, pair.get_left())

self.assertEqual(5, pair.middle)
self.assertEqual(5, pair.get_middle())

self.assertEqual(6, pair.right)
self.assertEqual(6, pair.get_right())

self.assertEqual("(1, 5, 6)", str(pair))
self.assertEqual("[1, 5, 6]", pair.to_string("[{0}, {1}, {2}]"))


class TestMutableTriple(TestCase):
def test_triple_methods(self):
pair: MutableTriple[int, int] = MutableTriple(1, 5, 6)
self.assertEqual(1, pair.left)
self.assertEqual(1, pair.get_left())

self.assertEqual(5, pair.middle)
self.assertEqual(5, pair.get_middle())

self.assertEqual(6, pair.right)
self.assertEqual(6, pair.get_right())

self.assertEqual("(1, 5, 6)", str(pair))
self.assertEqual("[1, 5, 6]", pair.to_string("[{0}, {1}, {2}]"))

pair.left = 2
self.assertEqual(2, pair.left)

pair.middle = 3
self.assertEqual(3, pair.middle)

pair.right = 7
self.assertEqual(7, pair.right)

pair.set_left(3)
self.assertEqual(3, pair.left)

pair.set_middle(0)
self.assertEqual(0, pair.middle)

pair.set_right(8)
self.assertEqual(8, pair.right)