Underscores in Python numbers

Bengt Richter bokr at oz.net
Sun Nov 20 21:53:39 EST 2005


On Sun, 20 Nov 2005 15:37:40 +0000, Steve Holden <steve at holdenweb.com> wrote:

>David M. Cooke wrote:
>> Peter Hansen <peter at engcorp.com> writes:
>> 
>> 
>>>Steven D'Aprano wrote:
>>>
>>>>Dealing with numeric literals with lots of digits is
>>>>a real (if not earth-shattering) human interface problem: it is hard for
>>>>people to parse long numeric strings.
>>>
>>>I'm totally unconvinced that this _is_ a real problem, if we define 
>>>"real" as being even enough to jiggle my mouse, let alone shattering the 
>>>planet.
>>>
>>>What examples does anyone have of where it is necessary to define a 
>>>large number of large numeric literals?  Isn't it the case that other 
>>>than the odd constants in various programs, defining a large number of 
>>>such values would be better done by creating a data file and parsing
>>>it?
>> 
>> 
>> One example I can think of is a large number of float constants used
>> for some math routine. In that case they usually be a full 16 or 17
>> digits. It'd be handy in that case to split into smaller groups to
>> make it easier to match with tables where these constants may come
>> from. Ex:
>> 
>> def sinxx(x):
>>     "computes sin x/x for 0 <= x <= pi/2 to 2e-9"
>>     a2 = -0.16666 66664
>>     a4 =  0.00833 33315
>>     a6 = -0.00019 84090
>>     a8 =  0.00000 27526
>>     a10= -0.00000 00239
>>     x2 = x**2
>>     return 1. + x2*(a2 + x2*(a4 + x2*(a6 + x2*(a8 + x2*a10))))
>> 
>> (or least that's what I like to write). Now, if I were going to higher
>> precision, I'd have more digits of course.
>> 
>Right, this is clearly such a frequent use case it's worth changing the 
>compiler for.
>
Agreed, let the OP hack a special-purpose solution for a special-case problem, e.g.
(tested just enough to show the idea on this post ;-)

 >>> def fundef(funsrc, preprocess=None):
 ...     def doprep(f):
 ...         protect_f = {}
 ...         try:
 ...             src = funsrc.lstrip().rstrip()+'\n' # remove leading/trailing blank lines
 ...             exec (preprocess and preprocess(src) or src) in protect_f
 ...             return type(f)(protect_f[f.func_name].func_code,
 ...                 f.func_globals, f.func_name, f.func_defaults, f.func_closure)
 ...         except Exception, e:
 ...             raise ValueError, 'Unable to translate %r ... due to\n%s'%(
 ...                 funsrc.lstrip().splitlines()[0][:30], '%s: %s'%(e.__class__.__name__, e))
 ...     return doprep
 ...

Now that decorator allows you to pre-process a function source
(e.g. according to what the OP might want for a preprocess argument):

 >>> import re
 >>> numgap = re.compile(r'(\d+) (?=\d)')
 >>> def prep(s): return numgap.sub(r'\1', s)
 ...

Then use it on the OP's example [1]

 >>> @fundef("""
 ... def sinxx(x):
 ...     "computes sin x/x for 0 <= x <= pi/2 to 2e-9"
 ...     a2 = -0.16666 66664
 ...     a4 =  0.00833 33315
 ...     a6 = -0.00019 84090
 ...     a8 =  0.00000 27526
 ...     a10= -0.00000 00239
 ...     x2 = x**2
 ...     return 1. + x2*(a2 + x2*(a4 + x2*(a6 + x2*(a8 + x2*a10))))
 ... """, preprocess=prep)
 ... def sinxx(x): pass # (defines signature and defaults in current scope)
 ...

[1] Note that no editing of previous source
is required except bracketing like

 @fundef("""
 <previous source here>
 ... """, preprocess=prep)
 <def-line from previous source with pass after the ':'>

That last line is required for the decorator, but also (I think, not tested) defines
the signature and arg defaults in the current scope, as opposed to where exec compiles
in the decorator.

 >>> sinxx
 <function sinxx at 0x02EFE4C4>
 >>> import math
 >>> def mxx(x): return x and math.sin(x)/x or 1.0
 ...
 >>> sinxx(0), mxx(0)
 (1.0, 1.0)
 >>> sinxx(.1), mxx(.1)
 (0.99833416647076856, 0.99833416646828155)
 >>> sinxx(.2), mxx(.2)
 (0.99334665398326816, 0.99334665397530608)
 >>>

Seems to work, approximately ;-)

 >>> @fundef("""
 ... def poo()
 ...     syntax problems
 ... """)
 ... def poo(): pass
 ...
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 10, in doprep
 ValueError: Unable to translate 'def poo()' ... due to
 SyntaxError: invalid syntax (line 1)
 
Some hope of somewhat useful exceptions too ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list