-----Original Message----- From: Chris Angelico <rosuav@gmail.com> Sent: 05 March 2020 11:42 To: python-ideas@python.org Subject: [Python-ideas] Re: IDEA: Allow override of all assignments of a built in type to be a different type On Thu, Mar 5, 2020 at 8:27 PM Steve Barnes <GadgetSteve@live.co.uk> wrote:
One of the lovely things about Python is that we have the capability to avoid issues such as the vagaries of the floating point type with libraries such as decimal and fractions. This is wonderous to me but comes with an issue that I suspect is limiting its usage. That issue is that you have to litter your code with constructors of those types, e.g.
from decimal import * # I know why this is bad but it comes straight from the examples a = Decimal("0.1") # Needs to be a string to avoid being temporarily a float b = Decimal("0.2") # Ditto c = Decimal("0.3") # Ditto
a + b == c # Magic this works!
As an aside, this complaint comes up in basically every language, and it's a consequence of the literal 0.1 not actually meaning one tenth. Nobody would bat an eyelid if you show that, say: x = 0.3333333 # one third y = 0.6666666 # two thirds x + y != 1.0 because they've obviously been rounded (those are NOT complete representations of those fractions). It's only with fifths and tenths that people are surprised, and only because they don't understand that computers work in binary :) That said, though, there are a number of good reasons for wanting to change the interpretation of literals. But by the time you get to executing the module code, it's too late - the literals get parsed and compiled into the code object, and they're already the values they are going to ultimately be. There are two approaches that would reasonably plausibly work for this. It's possible to redefine literal parsing using a future directive ("from __future__ import unicode_literals"), but those have to be defined entirely by the language. To do it in custom code, you'd need instead to run some code before your module is parsed - for example, an import hook. This would have to NOT be done by default - you'd have to do it on a per-module basis - because any change like this would break a lot of things. (Even if you keep it just to your module, using Decimal can create bizarre situations - such as where the average of two numbers is not between them.) Ultimately, I think the best solution in current Python is to just import Decimal as D and then use the shorter name. Hmm, is there a PEP regarding Decimal literals? I couldn't find one, although there is PEP 240 regarding rational literals. Maybe it's time to write up a rejected PEP explaining exactly what the problems are with Decimal literals. From memory, the problems are (a) it'd effectively require the gigantic decimal module to be imported by default, and (b) contexts don't work with literals. But since Decimal literals are such an obvious solution to most of the above problems, I think it'd be good to have a document saying why it won't help. ChrisA [Steve Barnes] I think that a part of the problem is that because Decimal silently accepts both float and string literal inputs things get surprising e.g.: In [5]: D(0.3) == D('0.3') Out[5]: False In [6]: D(0.5) == D('0.5') Out[6]: True In [7]: D(0.1) * 10 == 1 Out[7]: False In [8]: D(0.5) * 2 == 1 Out[8]: True Maybe a way forwards, (if we have no widely applicable way of preventing the actual call to the initialiser being interpreted as a float before it is seen), would be to: 1. Deprecate the use of the Decimal initialiser with float inputs so that decimal.Decimal(0.1), etc., issues a warning 2. Possibly have a from __future__ import NoDecimalFloat or similar 3. Eventually outlaw float as an input to the initialiser. While this would "break" code which happen to work as expected, e.g. decimal.Decimal(0.5) but at least we would have consistent behaviour and fail early rather than potentially working but giving incorrect or inconsistent results. While a rejected PEP would give somewhere to point to for explanations I am afraid that all to many python users never read any of the PEPs or only PEP8. Steve