@@ -37,11 +37,36 @@ class Number(Atom, ImmutableValueMixin, NumericOperators):
3737 being: Integer, Rational, Real, Complex.
3838 """
3939
40+ def __getnewargs__ (self ):
41+ """
42+ __getnewargs__ is used in pickle loading to ensure __new__ is
43+ called with the right value.
44+
45+ Most of the time a number takes one argument - its value
46+ When there is a kind of number, like Rational, or Complex,
47+ that has more than one argument, it should define this method
48+ accordingly.
49+ """
50+ return (self ._value ,)
51+
4052 def __str__ (self ) -> str :
4153 return str (self .value )
4254
43- def is_numeric (self , evaluation = None ) -> bool :
44- return True
55+ # FIXME: can we refactor or subclass objects to remove pattern_sort?
56+ def get_sort_key (self , pattern_sort = False ) -> tuple :
57+ """
58+ get_sort_key is used in Expression evaluation to determine how to
59+ order its list of elements. The tuple returned contains
60+ rank orders for different level as is found in say
61+ Python version release numberso or Python package version numbers.
62+
63+ This is the default routine for Number. Subclasses of Number like
64+ Complex may need to define this differently.
65+ """
66+ if pattern_sort :
67+ return super ().get_sort_key (True )
68+ else :
69+ return (0 , 0 , self ._value , 0 , 1 )
4570
4671 @property
4772 def is_literal (self ) -> bool :
@@ -51,8 +76,25 @@ def is_literal(self) -> bool:
5176 """
5277 return True
5378
79+ def is_numeric (self , evaluation = None ) -> bool :
80+ return True
81+
82+ def to_mpmath (self ):
83+ """
84+ Convert self._value to an mnpath number.
85+
86+ This is the default implementation for Number.
87+ There are kinds of numbers, like Rational, or Complex, that
88+ need to work differently than this default, and they will
89+ change the implementation accordingly.
90+ """
91+ return mpmath .mpf (self ._value )
92+
5493 @property
55- def value (self ) -> bool :
94+ def value (self ):
95+ """
96+ Returns this number's value.
97+ """
5698 return self ._value
5799
58100
@@ -101,27 +143,35 @@ class Integer(Number):
101143 value : int
102144 class_head_name = "System`Integer"
103145
104- # Collection of Integers defined so far.
146+ # Dictionary of Integer constant values defined so far.
105147 # We use this for object uniqueness.
148+ # The key is the Integer's Python `int` value, and the
149+ # dictionary's value is the corresponding Mathics Integer object.
106150 _integers = {}
107151
108- # We use __new__ here to unsure that two Integer's that have the same value
109- # return the same object.
152+ # We use __new__ here to ensure that two Integer's that have the same value
153+ # return the same object, and to set an object hash value.
154+ # Consider also @lru_cache, and mechanisms for limiting and
155+ # clearing the cache and the object store which might be useful in implementing
156+ # Builtin Share[].
110157 def __new__ (cls , value ) -> "Integer" :
111158
112159 n = int (value )
113- key = (cls , n )
114- self = cls ._integers .get (key )
160+ self = cls ._integers .get (value )
115161 if self is None :
116162 self = super ().__new__ (cls )
117163 self ._value = n
118164
119165 # Cache object so we don't allocate again.
120- self ._integers [key ] = self
166+ self ._integers [value ] = self
121167
122168 # Set a value for self.__hash__() once so that every time
123- # it is used this is fast.
124- self .hash = hash (key )
169+ # it is used this is fast. Note that in contrast to the
170+ # cached object key, the hash key needs to be unique across all
171+ # Python objects, so we include the class in the
172+ # event that different objects have the same Python value
173+ self .hash = hash ((cls , n ))
174+
125175 return self
126176
127177 def __eq__ (self , other ) -> bool :
@@ -145,6 +195,8 @@ def __gt__(self, other) -> bool:
145195 else super ().__gt__ (other )
146196 )
147197
198+ # __hash__ is defined so that we can store Number-derived objects
199+ # in a set or dictionary.
148200 def __hash__ (self ):
149201 return self .hash
150202
@@ -188,9 +240,6 @@ def make_boxes(self, form) -> "String":
188240 def to_sympy (self , ** kwargs ):
189241 return sympy .Integer (self ._value )
190242
191- def to_mpmath (self ):
192- return mpmath .mpf (self ._value )
193-
194243 def to_python (self , * args , ** kwargs ):
195244 return self .value
196245
@@ -211,21 +260,12 @@ def sameQ(self, other) -> bool:
211260 """Mathics SameQ"""
212261 return isinstance (other , Integer ) and self ._value == other .value
213262
214- def get_sort_key (self , pattern_sort = False ) -> tuple :
215- if pattern_sort :
216- return super ().get_sort_key (True )
217- else :
218- return (0 , 0 , self ._value , 0 , 1 )
219-
220263 def do_copy (self ) -> "Integer" :
221264 return Integer (self ._value )
222265
223266 def user_hash (self , update ):
224267 update (b"System`Integer>" + str (self ._value ).encode ("utf8" ))
225268
226- def __getnewargs__ (self ):
227- return (self ._value ,)
228-
229269 def __neg__ (self ) -> "Integer" :
230270 return Integer (- self ._value )
231271
@@ -304,11 +344,6 @@ def __ne__(self, other) -> bool:
304344 def atom_to_boxes (self , f , evaluation ):
305345 return self .make_boxes (f .get_name ())
306346
307- def get_sort_key (self , pattern_sort = False ) -> tuple :
308- if pattern_sort :
309- return super ().get_sort_key (True )
310- return (0 , 0 , self ._value , 0 , 1 )
311-
312347 def is_nan (self , d = None ) -> bool :
313348 return isinstance (self .value , sympy .core .numbers .NaN )
314349
@@ -326,32 +361,36 @@ class MachineReal(Real):
326361 Stored internally as a python float.
327362 """
328363
329- # Collection of MachineReals defined so far.
364+ # Dictionary of MachineReal constant values defined so far.
330365 # We use this for object uniqueness.
366+ # The key is the MachineReal's Python `float` value, and the
367+ # dictionary's value is the corresponding Mathics MachineReal object.
331368 _machine_reals = {}
332369
333370 def __new__ (cls , value ) -> "MachineReal" :
334371 n = float (value )
335372 if math .isinf (n ) or math .isnan (n ):
336373 raise OverflowError
337374
338- key = (cls , n )
339- self = cls ._machine_reals .get (key )
375+ self = cls ._machine_reals .get (n )
340376 if self is None :
341377 self = Number .__new__ (cls )
342378 self ._value = n
343379
344380 # Cache object so we don't allocate again.
345- self ._machine_reals [key ] = self
381+ self ._machine_reals [n ] = self
346382
347383 # Set a value for self.__hash__() once so that every time
348- # it is used this is fast.
349- self .hash = hash (key )
350- return self
384+ # it is used this is fast. Note that in contrast to the
385+ # cached object key, the hash key needs to be unique across all
386+ # Python objects, so we include the class in the
387+ # event that different objects have the same Python value
388+ self .hash = hash ((cls , n ))
351389
352- def __getnewargs__ (self ):
353- return (self .value ,)
390+ return self
354391
392+ # __hash__ is defined so that we can store Number-derived objects
393+ # in a set or dictionary.
355394 def __hash__ (self ):
356395 return self .hash
357396
@@ -424,13 +463,6 @@ def to_python(self, *args, **kwargs) -> float:
424463 def to_sympy (self , * args , ** kwargs ):
425464 return sympy .Float (self .value )
426465
427- def to_mpmath (self ):
428- return mpmath .mpf (self .value )
429-
430- @property
431- def value (self ) -> bool :
432- return self ._value
433-
434466
435467MachineReal0 = MachineReal (0 )
436468
@@ -444,32 +476,35 @@ class PrecisionReal(Real):
444476 Note: Plays nicely with the mpmath.mpf (float) type.
445477 """
446478
447- # Collection of MachineReals defined so far.
479+ # Dictionary of PrecisionReal constant values defined so far.
448480 # We use this for object uniqueness.
481+ # The key is the PrecisionReal's sympy.Float, and the
482+ # dictionary's value is the corresponding Mathics PrecisionReal object.
449483 _precision_reals = {}
450484
451485 value : sympy .Float
452486
453487 def __new__ (cls , value ) -> "PrecisionReal" :
454488 n = sympy .Float (value )
455- key = (cls , n )
456- self = cls ._precision_reals .get (key )
489+ self = cls ._precision_reals .get (n )
457490 if self is None :
458491 self = Number .__new__ (cls )
459492 self ._value = n
460493
461494 # Cache object so we don't allocate again.
462- self ._precision_reals [key ] = self
495+ self ._precision_reals [n ] = self
463496
464497 # Set a value for self.__hash__() once so that every time
465- # it is used this is fast.
466- self .hash = hash (key )
498+ # it is used this is fast. Note that in contrast to the
499+ # cached object key, the hash key needs to be unique across all
500+ # Python objects, so we include the class in the
501+ # event that different objects have the same Python value.
502+ self .hash = hash ((cls , n ))
467503
468504 return self
469505
470- def __getnewargs__ (self ):
471- return (self .value ,)
472-
506+ # __hash__ is defined so that we can store Number-derived objects
507+ # in a set or dictionary.
473508 def __hash__ (self ):
474509 return self .hash
475510
@@ -527,20 +562,16 @@ def to_python(self, *args, **kwargs):
527562 def to_sympy (self , * args , ** kwargs ):
528563 return self .value
529564
530- def to_mpmath (self ):
531- return mpmath .mpf (self .value )
532-
533- @property
534- def value (self ):
535- return self ._value
536-
537565
538566class ByteArrayAtom (Atom , ImmutableValueMixin ):
539567 value : str
540568 class_head_name = "System`ByteArrayAtom"
541569
542- # We use __new__ here to unsure that two ByteArrayAtom's that have the same value
543- # return the same object.
570+ # We use __new__ here to ensure that two ByteArrayAtom's that have the same value
571+ # return the same object, and to set an object hash value.
572+ # Consider also @lru_cache, and mechanisms for limiting and
573+ # clearing the cache and the object store which might be useful in implementing
574+ # Builtin Share[].
544575 def __new__ (cls , value ):
545576 self = super ().__new__ (cls )
546577 if type (value ) in (bytes , bytearray ):
@@ -629,8 +660,18 @@ class Complex(Number):
629660 real : Type [Number ]
630661 imag : Type [Number ]
631662
663+ # Dictionary of Complex constant values defined so far.
664+ # We use this for object uniqueness.
665+ # The key is the Complex value's real and imaginary parts as a tuple,
666+ # dictionary's value is the corresponding Mathics Complex object.
667+ _complex_numbers = {}
668+
669+ # We use __new__ here to ensure that two Integer's that have the same value
670+ # return the same object, and to set an object hash value.
671+ # Consider also @lru_cache, and mechanisms for limiting and
672+ # clearing the cache and the object store which might be useful in implementing
673+ # Builtin Share[].
632674 def __new__ (cls , real , imag ):
633- self = super ().__new__ (cls )
634675 if isinstance (real , Complex ) or not isinstance (real , Number ):
635676 raise ValueError ("Argument 'real' must be a Real number." )
636677 if imag is SymbolInfinity :
@@ -646,14 +687,26 @@ def __new__(cls, real, imag):
646687 if isinstance (imag , MachineReal ) and not isinstance (real , MachineReal ):
647688 real = real .round ()
648689
649- self .real = real
650- self .imag = imag
690+ value = (real , imag )
691+ self = cls ._complex_numbers .get (value )
692+ if self is None :
651693
652- # Set a value for self.__hash__() once so that every time
653- # it is used this is fast.
654- self .hash = hash (("Complex" , real , imag ))
694+ self = super ().__new__ (cls )
695+ self .real = real
696+ self .imag = imag
697+
698+ self ._value = value
699+
700+ # Cache object so we don't allocate again.
701+ self ._complex_numbers [value ] = self
702+
703+ # Set a value for self.__hash__() once so that every time
704+ # it is used this is fast. Note that in contrast to the
705+ # cached object key, the hash key needs to be unique across all
706+ # Python objects, so we include the class in the
707+ # event that different objects have the same Python value
708+ self .hash = hash ((cls , value ))
655709
656- self ._value = (self .real , self .imag )
657710 return self
658711
659712 def __hash__ (self ):
@@ -684,7 +737,14 @@ def default_format(self, evaluation, form) -> str:
684737 self .imag .default_format (evaluation , form ),
685738 )
686739
740+ # Note we can
687741 def get_sort_key (self , pattern_sort = False ) -> tuple :
742+ """
743+ get_sort_key is used in Expression evaluation to determine how to
744+ order its list of elements. The tuple returned contains
745+ rank orders for different level as is found in say
746+ Python version release numberso or Python package version numbers.
747+ """
688748 if pattern_sort :
689749 return super ().get_sort_key (True )
690750 else :
@@ -775,8 +835,11 @@ class Rational(Number):
775835 # Collection of integers defined so far.
776836 _rationals = {}
777837
778- # We use __new__ here to unsure that two Rationals's that have the same value
779- # return the same object.
838+ # We use __new__ here to ensure that two Rationals's that have the same value
839+ # return the same object, and to set an object hash value.
840+ # Consider also @lru_cache, and mechanisms for limiting and
841+ # clearing the cache and the object store which might be useful in implementing
842+ # Builtin Share[].
780843 def __new__ (cls , numerator , denominator = 1 ) -> "Rational" :
781844
782845 value = sympy .Rational (numerator , denominator )
@@ -795,6 +858,8 @@ def __new__(cls, numerator, denominator=1) -> "Rational":
795858 self .hash = hash (key )
796859 return self
797860
861+ # __hash__ is defined so that we can store Number-derived objects
862+ # in a set or dictionary.
798863 def __hash__ (self ):
799864 return self .hash
800865
@@ -806,9 +871,6 @@ def atom_to_boxes(self, f, evaluation):
806871 def to_sympy (self , ** kwargs ):
807872 return self .value
808873
809- def to_mpmath (self ):
810- return mpmath .mpf (self .value )
811-
812874 def to_python (self , * args , ** kwargs ) -> float :
813875 return float (self .value )
814876
0 commit comments