Julia has these kind of builtin things. The main problem is backward compatibility. However your tool is useful as a python-to-python parser (from the) (I remember some static analysis tools like "mpy"?)
pip install funcoperators <https://pypi.org/project/funcoperators/> solve the problem differently : From: (1/2).denominator -} To: (1 /frac/ 2).denominator With: frac = infix(Fraction) With: from Fraction import fractions Le mar. 18 mai 2021 à 09:40, Martin Teichmann <martin.teichm...@gmail.com> a écrit : > Hi List, > > some days ago I posted about my idea let integer division result in > fractions, not floats. The upshot of the discussion was that it is a pity > that we do not have literals for fractions, but that there is nothing to do > about it, as all proposed syntaxes were a bit ugly. > > But why do we need to have different syntax for both, isn't it possible to > simply do both at the same time? The interesting answer is: yes. So my > proposal is: number literals (which are not integers) are both fractions > and floats a the same time - only when we start calculating with them, the > receiving function will pick whatever it prefers. For backwards > compatiblity the default is float - but you may write code that looks at > the fraction as well. > > I prototyped that here: https://github.com/tecki/cpython/tree/ratiofloat > > The idea is the following: when the parser (technically, the AST > optimizer) creates the objects that represents the literals, let it add > some bread crumbs as to where those data are coming from. Currently, 1/2 > just means the float 0.5. Instead, let it be the object of a new class, I > dubbed it ratiofloat, which inherits from float, but has the exact value > added to it as well. ratiofloat just adds two C ints to the float class, > making it a bit bigger. But as I said, only for literals, calculated floats > still have the same size as before. > > To give an example (this is not fake, but from the prototype): > > >>> 2/5 > 0.4 > >>> (2/5).denominator > 5 > >>> isinstance(2/5, float) > True > >>> type(2/5) > <class 'ratiofloat'> > > Note that this is only done at compile time, no such behavior is done at > run time, everything just behaves like normal floats: > > >>> two=2 > >>> five=5 > >>> (two/five) > 0.4 > >>> (two/five).numerator > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > AttributeError: 'float' object has no attribute 'numerator' > > I have tested this, and all tests pass except those where it is explicitly > tested whether a value is a float, not one of its subclasses. I ran that > also through numpy and sympy, and both behave mostly fine, except again > where the tests are testing whether something is actually a float. > > All of this does not only work for integers, but also for float literals: > > >>> a=1/3 + 0.1 > >>> a > 0.43333333333333335 > >>> a.numerator > 13 > >>> a.denominator > 30 > > All this is only interesting once you teach some classes about it. To give > an example here: > > >>> from decimal import Decimal > >>> Decimal(1/3) > Decimal('0.3333333333333333333333333333') > >>> Decimal(0.1) > Decimal('0.1') > >>> from fractions import Fraction > >>> Fraction(1/3) > Fraction(1, 3) > >>> Fraction(0.1) > Fraction(1, 10) > > I also tries to teach sympy about this. While I succeeded in general, > there were many tests that failed, and that for an interesting reason: the > sympy tests seem to assume that if you use a float, you want to tell sympy > to calculate numerically. So for the sympy tests, 0.1*x and x/10 are > something completely different. IMHO this is actually an abuse of features: > 0.1 simply is the same as one-tenth, and code should at least try to treat > it the same, even if it fails at that. Other than that, sympy works fine > (after I taught it): > > >>> from sympy import symbols > >>> x = symbols("x") > >>> 1.5*x > 3*x/2 > >>> x**(0.5) > sqrt(x) > > I think this is now all good enough to be wrapped in a PEP, Chris, can you > guide me through the bureaucracy? > > How would we go forward about this? The good news is that all that I > described happens at compile time, so we can use a good ole' "from > __future__ import" approach. So my suggestion is: implement it for 3.11, > but activate it only with a future import or an interpreter option for the > command line. This gives libraries time to adopt to this new style. For > 3.12, make this option the default for the command line. So we can tell > people to just switch that option off if it doesn't work. And in some far, > glorious future, when everybody has a from __future__ line in all of their > files, we can make it a default everywhere. > > Cheers > > Martin > _______________________________________________ > Python-ideas mailing list -- python-ideas@python.org > To unsubscribe send an email to python-ideas-le...@python.org > https://mail.python.org/mailman3/lists/python-ideas.python.org/ > Message archived at > https://mail.python.org/archives/list/python-ideas@python.org/message/PFAGBPLQ24SQ7Q2Z6ZKZOGLPUNXBRUUT/ > Code of Conduct: http://python.org/psf/codeofconduct/ >
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/B4EPUQA3GCAXYWB6YMMNAJPMWP5L3QUH/ Code of Conduct: http://python.org/psf/codeofconduct/