RE: [Python-Dev] Why aren't more things weak referencable
"Martin v. Löwis" wrote:
Raymond Hettinger wrote:
Instances of classes inheriting from str, tuple, etc cannot be weakly referenced. Does anyone know the reason for this?
In addition to the reason Christian gave, one (conceptually more important) reason is that strings can't participate in cycles. Weak references were introduced as a mechanism to avoid creating cyclic structures, so that "backward" links could be made weak references.
It appears that people have then been eager to add weakref support to other datatypes. IMO, they have been too eager. For example, I can't see a reason why Unicode objects should be weakly referencable (just as I can't see a reason for plain strings).
Never rule out "foolish" consistencies. I can imagine a system where multiple, heterogeneous values all get run through a weakref processor; to have string objects pass through without Yet Another try/except would be a design and maintenance boon. Quite similar to my current "pet peeve":
None > 3 False None > 'hoopy' False None > True False None > datetime.date(2004, 5, 31) Traceback (most recent call last): File "<interactive input>", line 1, in ? TypeError: can't compare datetime.date to NoneType
...writing an O-R mapper, this particular hobgoblin bites me rather often ;) Robert Brewer MIS Amor Ministries fumanchu@amor.org
On Mon, May 31, 2004, Robert Brewer wrote:
Quite similar to my current "pet peeve":
None > 3 False None > 'hoopy' False None > True False None > datetime.date(2004, 5, 31) Traceback (most recent call last): File "<interactive input>", line 1, in ? TypeError: can't compare datetime.date to NoneType
...writing an O-R mapper, this particular hobgoblin bites me rather often ;)
Time for you to bite the bullet. Guido has all-but-decreed that the future of comparisons is that TypeError will be raised for all operators other than == and <> for types that have no appropriate relationship system. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "as long as we like the same operating system, things are cool." --piranha
Time for you to bite the bullet. Guido has all-but-decreed that the future of comparisons is that TypeError will be raised for all operators other than == and <> for types that have no appropriate relationship system.
When that happens, I hope that comparisons between long and float will either raise TypeError or behave as if they are done in infinite precision. Right now, the long gets converted to float, which may lose precision; as a result, it is possible to have values such that a==b, b==c, and a!=c.
When that happens, I hope that comparisons between long and float will either raise TypeError or behave as if they are done in infinite precision. Right now, the long gets converted to float, which may lose precision; as a result, it is possible to have values such that a==b, b==c, and a!=c.
I agree that doing them as if usin infinite precision is best, but I adamantly oppose the idea that they should not be comparable at all. To the contrary, we should do our best to ensure that int/long are properly and cleanly embedded in floats. That's why I introduced the change to int/long division after all. --Guido van Rossum (home page: http://www.python.org/~guido/)
I agree that doing them as if usin infinite precision is best, but I adamantly oppose the idea that they should not be comparable at all.
As do I.
To the contrary, we should do our best to ensure that int/long are properly and cleanly embedded in floats. That's why I introduced the change to int/long division after all.
Agreed. Unfortunately, long/float comparison doesn't work quite correctly right now:
n = 1 for i in range(2000): n += n n == 0.0 OverflowError: long int too large to convert to float
One strategy for solving the problem is to observe that for every floating-point implementation, there is a number N with the property that if x >= N, converting x from float to long preserves information, and if x <= N, converting x from long to float preserves information. Therefore, instead of unconditionally converting to float, the conversion's direction should be based on the value of one of the comparands. Of course, such comparisons can be made faster by doing a rough range check first, and doing the actual conversion only if the number of bits in the long is commensurate with the exponent of the float.
Unfortunately, long/float comparison doesn't work quite correctly right now:
n = 1 for i in range(2000): n += n n == 0.0 OverflowError: long int too large to convert to float
One strategy for solving the problem is to observe that for every floating-point implementation, there is a number N with the property that if x >= N, converting x from float to long preserves information, and if x <= N, converting x from long to float preserves information. Therefore, instead of unconditionally converting to float, the conversion's direction should be based on the value of one of the comparands.
Of course, such comparisons can be made faster by doing a rough range check first, and doing the actual conversion only if the number of bits in the long is commensurate with the exponent of the float.
Do you think you can come up with a patch, or at least a description of an algorithm that someone without a wizard level understanding of the issues could implement? --Guido van Rossum (home page: http://www.python.org/~guido/)
Can't you do it like so: def cmp_long_vs_float(l, f): try: lf = float(l) except OverflowError: if special_float_value(f): # NaN, Inf return cmp(0.0, f) return cmp(l, 0L) else: return cmp(lf, f) The try/else control flow seems to be what Python does today. If the OverflowError is triggered, then the magnitude of l is bigger than any finite float so you might just as well compare it against 0L. Infinite floats are handled by the "if special_float_value(f):" branch, or just ignored if you prefer.. (but having 2L**2000 compare greater than +INF would bring complaints) If it turns out to be more efficient, the implementation could LBYL to see whether l can convert to float without overflow. Jeff
Do you think you can come up with a patch, or at least a description of an algorithm that someone without a wizard level understanding of the issues could implement?
The following code is lightly but not exhaustively tested, but I do think it gives a good idea of the algorithm. In particular, note that compare(fmagic, lmagic+1) is -1 even though (fmagic==lmagic+1) yields True. lmagic = 1 fmagic = 1.0 while fmagic + 1.0 != fmagic: fmagic += fmagic lmagic += lmagic assert fmagic + 1.0 == fmagic assert fmagic - 1.0 != fmagic # fmagic and lmagic are the float and long representations of the # smallest power of 2 for which the floating-point LSB is strictly # greater than 1. Accordingly, adding 1 to this magic value doesn't # change it, but subtracting 1 does (because it is a power of 2, so # the subtraction makes the representation one bit shorter). # Because of the properties of fmagic and lmagic, converting an # integer that is < lmagic to floating-point will lose no information, # and converting a floating-point number that is >= fmagic to long # will also lose no information. def sign(x): if x > 0: return 1 if x < 0: return -1 return 0 def simple_compare(x, y): # arguments both float or neither if x < y: return -1 if x > y: return 1 return 0 def compare(x, y): # We handle only integers and floats if type(x) not in [int, long, float]: raise TypeError, "left argument to compare not integer or float" if type(y) not in [int, long, float]: raise TypeError, "right argument to compare not integer or float" # If the arguments are the same type (counting int and long as # the same), there's nothing special to do if (type(x) == float) == (type(y) == float): return simple_compare(x, y) # Perhaps the signs will make the comparison unnecessary signx = sign(x) signy = sign(y) if signx != signy or signx == 0: return simple_compare(signx, signy) # Now we know that both arguments are nonzero and have the same # sign. We'll use signx to remember the original sign and make # the arguments both positive for convenience. if signx < 0: x = -x y = -y # We have reduced the problem to one where the arguments are both # positive and exactly one of them is float--but we don't know # which one. For convenience, we'll make x float by swapping x # and y if necessary, adjusting signx as appropriate. if type(y) == float: x, y = y, x signx = -signx assert type(x) == float assert type(y) in [int, long] assert x > 0 and y > 0 # If x and y are on different sides of the magic barrier, we're # done. if x >= fmagic and y < lmagic: return signx if x < fmagic and y >= lmagic: return -signx # Now we know that x and y are on the same side of the barrier. # Depending on which side, we will convert x to long or y to # float, then compare. if x >= fmagic: x = long(x) else: y = float(y) return signx * simple_compare(x, y)
participants (5)
-
Aahz
-
Andrew Koenig
-
Guido van Rossum
-
Jeff Epler
-
Robert Brewer