Closed copperfield42 closed 2 years ago
I don't think you've implemented all the requisite methods of Real
. Specifically, you're missing an implementation for the __hash__
method. Consider adding something like this:
class MyNumber(Real):
# …
def __hash__(self) -> int:
return hash(self.value)
# …
Mypy probably would/should have detected this for you, possibly surfacing an error like Cannot instantiate abstract class "MyNumber" with abstract attribute "__hash__" [abstract]
for the line n = MyNumber(23)
.
oh my hash :o like the abc does not ask for it never occur to my that it was missing...
like the abc does not ask for it never occur to my that it was missing...
It's kind of buried. Note this (cf. this). You'll probably be hard pressed to write true numerics that aren't hash-able, but you're right: numerary
's …Like
protocols make defining a __hash__
method an explicit requirement. I should probably highlight that better in the docs.
I see, yeah is a little buried. Went I'm done with the required method from the abc and does what I want it to do I just call it a day, and the hash thing never crossed my mind...
I see, yeah is a little buried. Went I'm done with the required method from the abc and does what I want it to do I just call it a day, and the hash thing never crossed my mind...
I don't know if it's theoretically a strict requirement, but it's probably a practical one. I'll experiment with getting rid of it.
To be clearer, in most cases, it's desirable to use numbers with sets. Practically speaking, that means they need to be hashable. Consider:
>>> import poly
>>> r5 = poly.constante.RadicalConstante(5)
>>> poly.constante.RadicalConstante(5) in {r5}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'RadicalConstante'
I don't know if it's theoretically a strict requirement, but it's probably a practical one. I'll experiment with getting rid of it.
or you could add a new protocol that is more loose, that only check for the most basic of thing like idk, just the basic operations of + - / * and call it NumberLike
and that can be the functional equivalent to numbers.Numbers, after all Numbers itself is very loose, like extremely soo XD
>>> import numbers
>>> class N(numbers.Number):
... pass
...
>>> N()
<__main__.N object at 0x000001E95677FD30>
>>> isinstance(N(),numbers.Number)
True
>>>
To be clearer, in most cases, it's desirable to use numbers with sets. Practically speaking, that means they need to be hashable.
I guess, but I made it more for symbolic calculation like the sample Fibonnaci function
def fib(n):
"""calculate the nth number in the fibbonacci sequence"""
r5 = RadicalConstante(5) #sqrt(5)
g = (r5+1)//2 #golden ratio
return (g**n - (1-g)**n)//r5
That is the Binet's formula calculated without the pesky floating point problems so the result is exact no matter how big n is, so there is no need to touch floats because you know that the square root of 5 will banish at the end.
Or other such thing and you want to keep the root around or something and you don't want it to actualize into its decimal representation, so making it hashable was the least of my concern so I never notice it until now...
or you could add a new protocol that is more loose, that only check for the most basic of thing like idk, just the basic operations of + - / * and call it NumberLike
This is something numerary
clients can do for themselves by imitating the …Like
protocols (e.g., RealLike
):
from beartype.typing import Protocol, TypeVar, runtime_checkable
from numerary.types import CachingProtocolMeta, Supports…
T_co = TypeVar("T_co", covariant=True)
@runtime_checkable
class NumberType(
SupportsAbs[T_co],
SupportsRealOps[T_co],
SupportsComplexOps[T_co],
SupportsComplexPow[T_co],
Protocol[T_co],
metaclass=CachingProtocolMeta,
):
pass # no __hash__ method required
one: NumberType = 1
assert isinstance(one, NumberType)
one = "1" # properly results in assignment error
assert not isinstance(one, NumberType)
But, in reference to your MyNumber
subclass of Real
example above, be aware that numbers.Number
and derivatives also require a __hash__
method (cf. this).
consider this quick and dirty example
gives
I wonder why?