Thursday, October 10, 2013

PythonScript - Updates

One of the first changes I made this week to PythonScript was to change the default variable scope from global to local. PythonScript now follows the Python standard, where each function defines a new scope of local variables. The global keyword is now used to declare global variables when you need to assign a value to a global. The commit to do this is tiny, it simply injects var for you by looking at all variable assignments in a function.

I also added and improved some builtins: min, max, abs, chr, ord, tuple and dict. The new dict goes beyond what is possible in normal Python by allowing any type to be used as a key, so in case you ever had wanted to use a list or another dict as a dict key, now you can in PythonScript. See my commits here: [1], [2], [3]

Direct DOM

Another major missing feature of PythonScript was HTML DOM bindings, I first tried writting wrapper, but dropped that approach quickly when I found it was easy to modify the special get_attribute method to directly support DOM by generating wrapper functions at runtime. I was able to generalize this so that PythonScript can directly call other JavaScript functions, see this commit. The example below changes the text of button when clicked to 'CLICKED!' and inserts 'hello world' below it.
<script src="pythonscript.js"></script>
<button id="mybutton" onclick="test()">click me</button>

<script type="text/python">

a = 'hello'
b = 'world'

def test():
 con = document.createElement( 'div' )
 con.setAttribute('id', 'mydiv')
 print( con )
 txt = document.createTextNode( a+b )

 print( con.getAttribute('id') )
 btn = document.getElementById('mybutton')
 btn.firstChild.nodeValue = 'CLICKED!'



PythonScript now supports an array builtin that acts like array.array from Python's standard library. It is implemented using Javascripts ArrayBuffer and DataView API. If you are dealing with large sets of numbers, then using the new array builtin can save you memory, because in normal JavaScript each number is 64bits and costs you 8 bytes. When using array you can have a list-like-object that only uses 4 bytes (32bits) for each number it contains. See my commits here: [1], [2]

create a 32bit floating point array

arr = array('f', [0.1, 0.2] )
arr.append( 1.1 )
arr.append( 2.2 )
for value in arr:
 print( value )

As you can see above, the array class dynamically grows its internal ArrayBuffer for you when you append new items to it. This array will use half the memory of a normal JavaScript array, but can we do better?

create a 16bit floating point array

arr = array('float16', [-10.0, 3.141592653589793, 10.0])
print 'error:', arr[1] - 3.141592653589793

Above creates a special quantized array type. The DataView API has no notion of '16bit floats', but we can roll our own using Int16. Internally the array class finds the min and max the array will hold by looking at the array used to initialize it, then when packing and adding new values they are scaled and quantized. Later when accessing the elements of the array they get 'unscaled and unquantized' back into floats. The error between input and output is minimal for an array with minimal dynamic range. The example above has a dynamic range of -10 to 10, the error for PI is only 0.0000172, not bad. [1]

create an 8bit floating point array

arr = array('float8', [-10.0, 3.141592653589793, 10.0])
print 'error:', arr[1] - 3.141592653589793

For the ultimate memory savings you can use the special 8bit float type that only uses one byte per-number. This can be used for cases where a larger amount of error is acceptable, and the input data has values that are not far apart (low dynamic range). The above example, with the same dynamic range as the previous example produces a greater error of about 0.07. If the dynamic range is increased to -50 to 50, then the error increases to 0.38568 and PI becomes 2.7559.

No comments:

Post a Comment