1
|
|
|
from inspect import getmro |
2
|
|
|
from typing import ( |
3
|
|
|
Any, |
4
|
|
|
Type, |
5
|
|
|
Union, |
6
|
|
|
) |
7
|
|
|
|
8
|
|
|
import numpy |
9
|
|
|
from typish import Literal |
10
|
|
|
|
11
|
|
|
from nptyping.types._nptype import NPType, SimpleNPTypeMeta |
12
|
|
|
|
13
|
|
|
DEFAULT_INT_BITS = numpy.dtype(int).itemsize * 8 |
14
|
|
|
DEFAULT_FLOAT_BITS = numpy.dtype(float).itemsize * 8 |
15
|
|
|
|
16
|
|
|
|
17
|
|
View Code Duplication |
class _NumberMeta(SimpleNPTypeMeta): |
|
|
|
|
18
|
|
|
""" |
19
|
|
|
Super metaclass for the Number class. |
20
|
|
|
""" |
21
|
|
|
base = None |
22
|
|
|
npbase = None |
23
|
|
|
_bits = None |
24
|
|
|
_hashes = {} |
25
|
|
|
_repr_args = None |
26
|
|
|
|
27
|
|
|
def __eq__(cls, other): |
28
|
|
|
return hash(cls) == hash(other) |
29
|
|
|
|
30
|
|
|
def __hash__(cls): |
31
|
|
|
key = (cls.base, cls.npbase, cls._bits) |
32
|
|
|
if key not in cls._hashes: |
33
|
|
|
cls._hashes[key] = int(numpy.prod([hash(elem) for elem in key])) |
34
|
|
|
return cls._hashes[key] |
35
|
|
|
|
36
|
|
|
def __instancecheck__(cls, instance: Any) -> bool: |
37
|
|
|
from nptyping.functions._get_type import get_type |
38
|
|
|
|
39
|
|
|
if cls == instance or type(instance) in (int, float): |
40
|
|
|
# Covers Python types. |
41
|
|
|
return True |
42
|
|
|
|
43
|
|
|
return issubclass(get_type(instance), cls) |
44
|
|
|
|
45
|
|
|
def __subclasscheck__(cls, subclass: type) -> bool: |
46
|
|
|
result = False |
47
|
|
|
if cls == subclass: |
48
|
|
|
result = True |
49
|
|
|
elif _is_a(subclass, Number): |
50
|
|
|
# Cover nptyping number types. |
51
|
|
|
result = _is_number_subclass_of(subclass, cls) |
52
|
|
|
elif _is_number_type(subclass): |
53
|
|
|
result = _is_numpy_or_python_type_subclass_of(subclass, cls) |
54
|
|
|
return result |
55
|
|
|
|
56
|
|
|
|
57
|
|
View Code Duplication |
class Number(NPType, metaclass=_NumberMeta): |
|
|
|
|
58
|
|
|
""" |
59
|
|
|
Superclass for number types (integers and floating point numbers). Can be |
60
|
|
|
optionally given the number of bits. |
61
|
|
|
""" |
62
|
|
|
base = None |
63
|
|
|
npbase = None |
64
|
|
|
_bits = None |
65
|
|
|
_repr_args = None |
66
|
|
|
|
67
|
|
|
@classmethod |
68
|
|
|
def _after_subscription(cls, args: Any) -> None: |
69
|
|
|
if isinstance(args, tuple): |
70
|
|
|
cls.base = args[0] |
71
|
|
|
cls.npbase = args[1] |
72
|
|
|
return |
73
|
|
|
|
74
|
|
|
if not isinstance(args, int): |
75
|
|
|
raise TypeError('Number takes only an int as generic type. ' |
76
|
|
|
'Given: {}'.format(type(args).__name__)) |
77
|
|
|
|
78
|
|
|
cls._bits = args |
79
|
|
|
cls._repr_args = args |
80
|
|
|
|
81
|
|
|
if not hasattr(numpy, '{}{}'.format(cls.base.__name__, cls._bits)): |
82
|
|
|
raise TypeError('Unsupported number of bits: {}'.format(args)) |
83
|
|
|
|
84
|
|
|
@classmethod |
85
|
|
|
def bits(cls) -> Union[int, Literal[Any]]: |
86
|
|
|
""" |
87
|
|
|
Return the number of bits of this Number type. |
88
|
|
|
:return: the number of bits or Any. |
89
|
|
|
""" |
90
|
|
|
return cls._bits |
91
|
|
|
|
92
|
|
|
|
93
|
|
View Code Duplication |
class Int(Number[int, numpy.signedinteger]): |
|
|
|
|
94
|
|
|
""" |
95
|
|
|
A (signed) numpy int. Can be given the number of bits optionally. |
96
|
|
|
|
97
|
|
|
>>> Int[32] |
98
|
|
|
Int[32] |
99
|
|
|
""" |
100
|
|
|
|
101
|
|
|
@classmethod |
102
|
|
|
def type_of(cls, obj: Any) -> Type['Int']: |
103
|
|
|
""" |
104
|
|
|
Return the NPType that corresponds to obj. |
105
|
|
|
:param obj: an int compatible object. |
106
|
|
|
:return: a Int type. |
107
|
|
|
""" |
108
|
|
|
from nptyping.functions._get_type import get_type_int |
109
|
|
|
return get_type_int(obj) |
110
|
|
|
|
111
|
|
|
@staticmethod |
112
|
|
|
def fitting(number: int) -> Type['Int']: |
113
|
|
|
""" |
114
|
|
|
Return the Int type that fits the given number. |
115
|
|
|
:param number: the number of which the Int type is to be found. |
116
|
|
|
:return: a type of Int. |
117
|
|
|
""" |
118
|
|
|
bitlen = number.bit_length() |
119
|
|
|
for bits in [8, 16, 32, 64]: |
120
|
|
|
if bitlen <= bits - 1: # subtract sign bit. |
121
|
|
|
break |
122
|
|
|
return Int[bits] |
|
|
|
|
123
|
|
|
|
124
|
|
|
|
125
|
|
View Code Duplication |
class UInt(Number[int, numpy.unsignedinteger]): |
|
|
|
|
126
|
|
|
""" |
127
|
|
|
An unsigned numpy int. Can be given the number of bits optionally. |
128
|
|
|
|
129
|
|
|
>>> UInt[32] |
130
|
|
|
UInt[32] |
131
|
|
|
""" |
132
|
|
|
|
133
|
|
|
@classmethod |
134
|
|
|
def type_of(cls, obj: Any) -> Type['UInt']: |
135
|
|
|
""" |
136
|
|
|
Return the NPType that corresponds to obj. |
137
|
|
|
:param obj: an uint compatible object. |
138
|
|
|
:return: an UInt type. |
139
|
|
|
""" |
140
|
|
|
from nptyping.functions._get_type import get_type_uint |
141
|
|
|
return get_type_uint(obj) |
142
|
|
|
|
143
|
|
|
@staticmethod |
144
|
|
|
def fitting(number: int) -> Type['UInt']: |
145
|
|
|
""" |
146
|
|
|
Return the UInt type that fits the given number. |
147
|
|
|
:param number: the number of which the UInt type is to be found. |
148
|
|
|
:return: a type of UInt. |
149
|
|
|
""" |
150
|
|
|
bitlen = number.bit_length() |
151
|
|
|
for bits in [8, 16, 32, 64]: |
152
|
|
|
if bitlen <= bits: |
153
|
|
|
break |
154
|
|
|
return UInt[bits] |
|
|
|
|
155
|
|
|
|
156
|
|
|
|
157
|
|
|
class Float(Number[float, numpy.floating]): |
158
|
|
|
""" |
159
|
|
|
A numpy float. Can be given the number of bits optionally. |
160
|
|
|
|
161
|
|
|
>>> Float[32] |
162
|
|
|
Float[32] |
163
|
|
|
""" |
164
|
|
|
|
165
|
|
|
@staticmethod |
166
|
|
|
def type_of(obj: Any) -> Type['Float']: |
167
|
|
|
""" |
168
|
|
|
Return the NPType that corresponds to obj. |
169
|
|
|
:param obj: a float compatible object. |
170
|
|
|
:return: a Float type. |
171
|
|
|
""" |
172
|
|
|
from nptyping.functions._get_type import get_type_float |
173
|
|
|
return get_type_float(obj) |
174
|
|
|
|
175
|
|
|
|
176
|
|
|
def _is_a(this: Any, that: type) -> bool: |
177
|
|
|
# Return whether this is a subclass of that, considering the mro. |
178
|
|
|
return that in getmro(this) |
179
|
|
|
|
180
|
|
|
|
181
|
|
|
def _is_number_subclass_of( |
182
|
|
|
subclass: Type[Number], |
183
|
|
|
superclass: Type[Number]) -> bool: |
184
|
|
|
# Return whether subclass (which must be a type of Number) subclasses |
185
|
|
|
# superclass. |
186
|
|
|
base_is_eq = (not superclass.npbase |
187
|
|
|
or issubclass(subclass.npbase, superclass.npbase)) |
188
|
|
|
bits_is_eq = not superclass.bits() or subclass.bits() == superclass.bits() |
189
|
|
|
return base_is_eq and bits_is_eq |
190
|
|
|
|
191
|
|
|
|
192
|
|
View Code Duplication |
def _is_numpy_or_python_type_subclass_of( |
|
|
|
|
193
|
|
|
subclass: Any, |
194
|
|
|
superclass: Type[Number]) -> bool: |
195
|
|
|
# Return whether subclass (which must be a numpy type or a Python type) |
196
|
|
|
# subclasses superclass. |
197
|
|
|
if not superclass.npbase: |
198
|
|
|
# superclass is Number. |
199
|
|
|
result = True |
200
|
|
|
else: |
201
|
|
|
try: |
202
|
|
|
nptype = superclass.type_of(subclass) |
203
|
|
|
except TypeError: |
204
|
|
|
result = False |
205
|
|
|
else: |
206
|
|
|
result = issubclass(nptype, superclass) |
207
|
|
|
return result |
208
|
|
|
|
209
|
|
|
|
210
|
|
|
def _is_number_type(type_: type) -> bool: |
211
|
|
|
# Return whether type_ is a numpy/Python number type. |
212
|
|
|
return (issubclass(type_, numpy.number) |
213
|
|
|
or issubclass(type_, int) |
214
|
|
|
or issubclass(type_, float)) |
215
|
|
|
|
216
|
|
|
|
217
|
|
|
Int8 = Int[8] |
218
|
|
|
Int16 = Int[16] |
219
|
|
|
Int32 = Int[32] |
220
|
|
|
Int64 = Int[64] |
221
|
|
|
|
222
|
|
|
UInt8 = UInt[8] |
223
|
|
|
UInt16 = UInt[16] |
224
|
|
|
UInt32 = UInt[32] |
225
|
|
|
UInt64 = UInt[64] |
226
|
|
|
|
227
|
|
|
Float16 = Float[16] |
228
|
|
|
Float32 = Float[32] |
229
|
|
|
Float64 = Float[64] |
230
|
|
|
|