Skip to content

Commit 85cfecc

Browse files
Add Tuple classes to pycommons-lang (#20)
* Add Tuple classes to pycommons-lang * Add Unittests - Pair and Triple * Fix code format
1 parent 833601f commit 85cfecc

File tree

6 files changed

+291
-0
lines changed

6 files changed

+291
-0
lines changed

pycommons/lang/tuple/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .pair import Pair, ImmutablePair, MutablePair
2+
from .triple import Triple, ImmutableTriple, MutableTriple
3+
4+
__all__ = ["Pair", "ImmutablePair", "MutablePair", "Triple", "ImmutableTriple", "MutableTriple"]

pycommons/lang/tuple/pair.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from __future__ import annotations
2+
3+
import typing
4+
from abc import ABC, abstractmethod
5+
from collections import UserList
6+
from typing import TypeVar, Generic, Union, Any
7+
8+
_L = TypeVar("_L")
9+
_R = TypeVar("_R")
10+
11+
12+
class Pair(ABC, Generic[_L, _R]):
13+
@classmethod
14+
def of(cls, left: _L, right: _R) -> ImmutablePair[_L, _R]:
15+
return ImmutablePair(left, right)
16+
17+
@property
18+
def left(self) -> _L:
19+
return typing.cast(_L, self[0])
20+
21+
@property
22+
def right(self) -> _R:
23+
return typing.cast(_R, self[1])
24+
25+
@property
26+
def key(self) -> _L:
27+
return self.left
28+
29+
@property
30+
def value(self) -> _R:
31+
return self.right
32+
33+
def get_left(self) -> _L:
34+
return self.left
35+
36+
def get_right(self) -> _R:
37+
return self.right
38+
39+
def get_key(self) -> _L:
40+
return self.left
41+
42+
def get_value(self) -> _R:
43+
return self.right
44+
45+
@abstractmethod
46+
def __getitem__(self, item: Any) -> Any:
47+
... # pragma: no cover
48+
49+
def __str__(self) -> str:
50+
return self.to_string("({0}, {1})")
51+
52+
def to_string(self, fmt: str) -> str:
53+
return fmt.format(self.left, self.right)
54+
55+
56+
class ImmutablePair(tuple, Pair[_L, _R], Generic[_L, _R]): # type: ignore
57+
def __getitem__(self, item: Any) -> Any:
58+
return typing.cast(Union[_L, _R], tuple.__getitem__(self, item))
59+
60+
def __new__(cls, left: _L, right: _R) -> ImmutablePair[_L, _R]:
61+
return super().__new__(cls, [left, right]) # type: ignore
62+
63+
64+
class MutablePair(UserList, Pair, Generic[_L, _R]): # type: ignore
65+
def __getitem__(self, item: Any) -> Any:
66+
return typing.cast(Union[_L, _R], UserList.__getitem__(self, item))
67+
68+
def __init__(self, left: _L, right: _R) -> None:
69+
super().__init__([left, right])
70+
71+
@property
72+
def left(self) -> _L:
73+
return typing.cast(_L, super().left)
74+
75+
@left.setter
76+
def left(self, left: _L) -> None:
77+
self[0] = left
78+
79+
@property
80+
def right(self) -> _R:
81+
return typing.cast(_R, super().right)
82+
83+
@right.setter
84+
def right(self, right: _R) -> None:
85+
self[1] = right
86+
87+
def set_left(self, key: _L) -> None:
88+
self.left = key
89+
90+
def set_right(self, value: _R) -> None:
91+
self.right = value

pycommons/lang/tuple/triple.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
from __future__ import annotations
2+
3+
import typing
4+
from abc import ABC, abstractmethod
5+
from collections import UserList
6+
from typing import TypeVar, Generic, Union, Tuple, Any
7+
8+
_L = TypeVar("_L")
9+
_M = TypeVar("_M")
10+
_R = TypeVar("_R")
11+
12+
13+
class Triple(ABC, Generic[_L, _M, _R]):
14+
@classmethod
15+
def of(cls, left: _L, middle: _M, right: _R) -> ImmutableTriple[_L, _M, _R]:
16+
return ImmutableTriple(left, middle, right)
17+
18+
@property
19+
def left(self) -> _L:
20+
return typing.cast(_L, self[0])
21+
22+
@property
23+
def middle(self) -> _M:
24+
return typing.cast(_M, self[1])
25+
26+
@property
27+
def right(self) -> _R:
28+
return typing.cast(_R, self[2])
29+
30+
def get_left(self) -> _L:
31+
return self.left
32+
33+
def get_middle(self) -> _M:
34+
return self.middle
35+
36+
def get_right(self) -> _R:
37+
return self.right
38+
39+
@abstractmethod
40+
def __getitem__(self, item: Any) -> Any: # pragma: no cover
41+
...
42+
43+
def __str__(self) -> str:
44+
return self.to_string("({0}, {1}, {2})")
45+
46+
def to_string(self, fmt: str) -> str:
47+
return fmt.format(self.left, self.middle, self.right)
48+
49+
50+
class ImmutableTriple(Tuple, Triple[_L, _M, _R], Generic[_L, _M, _R]): # type: ignore
51+
def __getitem__(self, item: Any) -> Any:
52+
return typing.cast(Union[_L, _M, _R], tuple.__getitem__(self, item))
53+
54+
def __new__(cls, left: _L, middle: _M, right: _R) -> ImmutableTriple[_L, _M, _R]:
55+
return super().__new__(cls, [left, middle, right]) # type: ignore
56+
57+
58+
class MutableTriple(UserList, Triple[_L, _M, _R], Generic[_L, _M, _R]): # type: ignore
59+
def __getitem__(self, item: Any) -> Any:
60+
return typing.cast(Union[_L, _M, _R], UserList.__getitem__(self, item))
61+
62+
def __init__(self, left: _L, middle: _M, right: _R) -> None:
63+
super().__init__([left, middle, right])
64+
65+
@property
66+
def left(self) -> _L:
67+
return super().left
68+
69+
@left.setter
70+
def left(self, left: _L) -> None:
71+
self[0] = left
72+
73+
@property
74+
def middle(self) -> _M:
75+
return super().middle
76+
77+
@middle.setter
78+
def middle(self, middle: _M) -> None:
79+
self[1] = middle
80+
81+
@property
82+
def right(self) -> _R:
83+
return super().right
84+
85+
@right.setter
86+
def right(self, right: _R) -> None:
87+
self[2] = right
88+
89+
def set_left(self, left: _L) -> None:
90+
self.left = left
91+
92+
def set_middle(self, middle: _M) -> None:
93+
self.middle = middle
94+
95+
def set_right(self, right: _R) -> None:
96+
self.right = right

tests/pycommons/lang/tuple/__init__.py

Whitespace-only changes.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from unittest import TestCase
2+
3+
from pycommons.lang.tuple import Pair, MutablePair
4+
5+
6+
class TestImmutablePair(TestCase):
7+
def test_pair_methods(self):
8+
pair: Pair[int, int] = Pair.of(1, 6)
9+
self.assertEqual(1, pair.left)
10+
self.assertEqual(1, pair.get_left())
11+
self.assertEqual(1, pair.get_key())
12+
13+
self.assertEqual(6, pair.right)
14+
self.assertEqual(6, pair.get_right())
15+
self.assertEqual(6, pair.get_value())
16+
17+
self.assertEqual("(1, 6)", str(pair))
18+
self.assertEqual("[1, 6]", pair.to_string("[{0}, {1}]"))
19+
20+
21+
class TestMutablePair(TestCase):
22+
def test_pair_methods(self):
23+
pair: MutablePair[int, int] = MutablePair(1, 6)
24+
self.assertEqual(1, pair.left)
25+
self.assertEqual(1, pair.get_left())
26+
self.assertEqual(1, pair.key)
27+
self.assertEqual(1, pair.get_key())
28+
29+
self.assertEqual(6, pair.right)
30+
self.assertEqual(6, pair.get_right())
31+
self.assertEqual(6, pair.value)
32+
self.assertEqual(6, pair.get_value())
33+
34+
self.assertEqual("(1, 6)", str(pair))
35+
self.assertEqual("[1, 6]", pair.to_string("[{0}, {1}]"))
36+
37+
pair.left = 2
38+
self.assertEqual(2, pair.left)
39+
40+
pair.right = 7
41+
self.assertEqual(7, pair.right)
42+
43+
pair.set_left(3)
44+
self.assertEqual(3, pair.left)
45+
46+
pair.set_right(8)
47+
self.assertEqual(8, pair.right)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from unittest import TestCase
2+
3+
from pycommons.lang.tuple import Triple, MutableTriple
4+
5+
6+
class TestImmutableTriple(TestCase):
7+
def test_triple_methods(self):
8+
pair: Triple[int, int] = Triple.of(1, 5, 6)
9+
self.assertEqual(1, pair.left)
10+
self.assertEqual(1, pair.get_left())
11+
12+
self.assertEqual(5, pair.middle)
13+
self.assertEqual(5, pair.get_middle())
14+
15+
self.assertEqual(6, pair.right)
16+
self.assertEqual(6, pair.get_right())
17+
18+
self.assertEqual("(1, 5, 6)", str(pair))
19+
self.assertEqual("[1, 5, 6]", pair.to_string("[{0}, {1}, {2}]"))
20+
21+
22+
class TestMutableTriple(TestCase):
23+
def test_triple_methods(self):
24+
pair: MutableTriple[int, int] = MutableTriple(1, 5, 6)
25+
self.assertEqual(1, pair.left)
26+
self.assertEqual(1, pair.get_left())
27+
28+
self.assertEqual(5, pair.middle)
29+
self.assertEqual(5, pair.get_middle())
30+
31+
self.assertEqual(6, pair.right)
32+
self.assertEqual(6, pair.get_right())
33+
34+
self.assertEqual("(1, 5, 6)", str(pair))
35+
self.assertEqual("[1, 5, 6]", pair.to_string("[{0}, {1}, {2}]"))
36+
37+
pair.left = 2
38+
self.assertEqual(2, pair.left)
39+
40+
pair.middle = 3
41+
self.assertEqual(3, pair.middle)
42+
43+
pair.right = 7
44+
self.assertEqual(7, pair.right)
45+
46+
pair.set_left(3)
47+
self.assertEqual(3, pair.left)
48+
49+
pair.set_middle(0)
50+
self.assertEqual(0, pair.middle)
51+
52+
pair.set_right(8)
53+
self.assertEqual(8, pair.right)

0 commit comments

Comments
 (0)