friendly_idle is done!
I've found a better solution for the remaining issue I had mentioned in the previous blog post.
I also found a fix for an "annoyance" mentioned by Raymond Hettinger on Twitter!
friendly_idle is done!
I've found a better solution for the remaining issue I had mentioned in the previous blog post.
I also found a fix for an "annoyance" mentioned by Raymond Hettinger on Twitter!
friendly_idle is now available. This is just a quick announcement. Eventually I plan to write a longer blog post explaining how I use import hooks to patch IDLE and to provide seamless support for friend/friendly_traceback. Before I incorporated "partial" support for IDLE within friendly, I had released a package named friendly_idle ... but this is really a much better version.
When you launch it from a terminal, the only clue you get that this is not your regular IDLE is from the window title.
Beginning programmers are often surprised by floating point arithmetic inaccuracies. If they use Python, many will often write posts saying that Python is "broken" when the see results as follows:
>>> 0.1 + 0.2 0.30000000000000004
This particular result is not limited to Python. In fact, it is so common that there exists a site with a name inspired by this example (0.30000000000000004.com/), devoted to explaining the origin of this puzzling result, followed by examples from many programming languages.
Python provides some alternatives to standard floating point operations. For example, one can use the decimal module to perform fixed point arithmetic operations. Here's an example.
>>> from decimal import Decimal, getcontext >>> getcontext().prec = 7 >>> Decimal(0.1) + Decimal(0.2) Decimal('0.3000000') >>> print(_) 0.3000000
While one can set the precision (number of decimals) with which operations are performed, printed values can carry extra zeros: 0.3000000 does not look as "nice" as 0.3.
Another alternative included with Python's standard library is the fractions module: it provides support for rational number arithmetic.
>>> from fractions import Fraction >>> Fraction("0.1") + Fraction("0.2") Fraction(3, 10) >>> print(_) 3/10
However, the fractions module can yield some surprising results if one does not use string arguments to represent floats, as was mentioned by Will McGugan (of Rich and Textual fame) in a recent tweet.
>>> from fractions import Fraction as F >>> F("0.1") Fraction(1, 10) >>> F(0.1) Fraction(3602879701896397, 36028797018963968)
In the second case, 0.1 is a float which means that it carries some intrinsic inaccuracy. For the first case, some parsing is done by Python to determine the number of decimal places to use before converting the result into a rational number. A similar result can be achieved using the limit_denominator method of the Fraction class:
>>> F(0.1).limit_denominator(10) Fraction(1, 10)
In fact, we do not have to be as restrictive in the limitation imposed on the denominator to achieve the same result
>>> F(0.1).limit_denominator(1_000_000_000) Fraction(1, 10)
While we can achieve some "more intuitive" results for floating point arithmetic using special modules from Python, the notation that one has to use is not as simple as "0.1 + 0.2". As Raymond Hettinger often says: "There has to be a better way."
As readers of this blog already know, I created a Python package named ideas to facilitate the creation of import hooks and to enable easy experimentation with modified Python syntax. ideas comes with its own console that support modified Python syntax. It can also be used with IPython (and thus with Jupyter notebooks).
Using ideas, one can "instruct" python to perform rational arithmetic. For example, suppose I have a Python file containing the following:
# simple_math.py a = 0.2 + 0.1 b = 0.2 + 1/10 c = 2/10 + 1/10 print(a, b, c)
I can run this with Python, getting the expected "unintuitive" result:
> py simple_math.py 0.30000000000000004 0.30000000000000004 0.30000000000000004
Alternatively, using ideas, I can execute this file using rational arithmetic:
> ideas simple_math -a rational_math 3/10 3/10 3/10
Using a different import hook, I can have the result shown with floating point notation.
> ideas simple_math -a nicer_floats 0.3 0.3 0.3
Instead of executing a script, let's use the ideas console instead, starting with "nicer_float"
ideas> 0.1 + 0.2 0.3 ideas> 1/10 + 2/10 0.3For "nicer_float", I've also adopted the Pyret's notation: floating-point number immediately preceded by "~" are treated as "approximate" floating points i.e. with the regular inaccuracy.
ideas> ~0.1 + 0.2 0.30000000000000004
And, as mentioned before, I can use ideas with IPython. Here's a very brief example
IPython 8.0.0b1 -- An enhanced Interactive Python. Type '?' for help. In [1]: from ideas.examples import rational_math In [2]: hook = rational_math.add_hook() The following initializing code from ideas is included: from fractions import Fraction In [3]: 0.1 + 0.2 Out[3]: Fraction(3, 10)
Given how confusing floating point arithmetic is to beginners, I think it would be nice if Python had an easy built-in way to switch modes and do calculations as done with ideas in the above examples. However, I doubt very much that this will ever happen. Fortunately, as demonstrated above, it is possible to use import hooks and modified interactive console to achieve this result.