pyscript用python写JavaScript

xxxspy 2018-07-13 20:17:55
Categories: Tags:

声明: 本文由DataScience发表,未经允许不得转载。 转载请注明本文链接mlln.cn, 并在文后留言转载.

本文代码运行环境:

pyscript简介

pyscript模块提供了将Python代码转换为JavaScript的功能。这是使用PyScript的简要介绍。有关详细信息,请参阅以下部分。

PyScript是一种使用Python语言编写JavaScript的工具。支持所有相关的内置功能,以及list,dict和str的方法。不支持set,带step参数的切片操作,yield和import。除此之外,大多数Python代码应该按预期工作…根据经验,代码在正确时应该按预期运行,但错误报告可能不是非常Pythonic。

您需要了解的最重要的函数是py2jsevalpy。原则上,您不需要JavaScript的知识来编写PyScript代码,尽管它在极端情况下确实有帮助。

目标

使用web技术实现可视化和用户交互的Python项目有所增加。 PyScript允许用户在Python中编写JavaScript回调,实现交互的灵活/快速。

这导致了以下两个主要目标:

通过让人们使用Python语法和内置函数来编写javascript,并修复一些JavaScripts怪癖,使编写JavaScript变得更容易,更不令人沮丧。允许在Python程序中定义JavaScript代码段。PyScript生成的代码可以独立工作。任何(PyScript兼容的)Python片段都可以转换为JS;你不需要另一个JS库来运行它。

PyScript还可用于开发独立的JavaScript(AMD)模块。

注意

PyScript修复了JS的一些怪癖,但它仍然只是JavaScript。这是一个值得关注的事项列表。此列表可能不完整。如果您打算大量使用PyScript,我们建议您熟悉JavaScript。

案例

为了在notebook中实时显示python转JavaScript的过程, 我们注册一个魔法函数, 功能很简单, 就是把python代码转成JavaScript, 并显示在输出区域。

1
2
3
4
5
6
7
8
9
from IPython.core.magic import register_cell_magic
from IPython.display import Markdown


@register_cell_magic
def py2js(line, cell):
from pscript import py2js
js= py2js(cell)
return Markdown(f'```javascript\n{js}\n```')
1
2
3
%%py2js

a = 1
1
2
var a;
a = 1;

基本操作

1
2
3
4
5
6
7
8
%%py2js

# Simple operations
3 + 4 -1
3 * 7 / 9
5**2
pow(5, 2)
7 // 2
1
2
3
4
5
6
var _pyfunc_pow = Math.pow;
(3 + 4) - 1;
(3 * 7) / 9;
Math.pow(5, 2);
_pyfunc_pow(5, 2);
Math.floor(7/2);

数据类型

1
2
3
4
5
6
%%py2js

[True, False, None]

foo = [1, 2, 3]
bar = {'a': 1, 'b': 2}
1
2
3
4
var bar, foo;
[true, false, null];
foo = [1, 2, 3];
bar = ({a: 1, b: 2});
1
2
3
4
%%py2js

a = [2,3,4]
a.sort()
1
2
3
4
5
6
7
8
9
10
11
var _pymeth_sort = function (key, reverse) { // nargs: 0 1 2
if (!Array.isArray(this)) return this.sort.apply(this, arguments);
var comp = function (a, b) {a = key(a); b = key(b);
if (a<b) {return -1;} if (a>b) {return 1;} return 0;};
comp = Boolean(key) ? comp : undefined;
this.sort(comp);
if (reverse) this.reverse();
};
var a;
a = [2, 3, 4];
_pymeth_sort.call(a, undefined, false);

切片操作和取值

1
2
3
4
5
%%py2js

foo = [1, 2, 3, 4, 5]
foo[2:]
foo[2:-2]
1
2
3
4
var foo;
foo = [1, 2, 3, 4, 5];
foo.slice(2);
foo.slice(2,-2);
1
2
3
4
%%py2js
bar = 'abcdefghij'
bar[2:]
bar[2:-2]
1
2
3
4
var bar;
bar = "abcdefghij";
bar.slice(2);
bar.slice(2,-2);
1
2
3
4
5
%%py2js
# Subscripting
foo = {'bar': 3}
foo['bar']
foo.bar # Works in JS, but not in Python
1
2
3
4
var foo;
foo = ({bar: 3});
foo["bar"];
foo.bar;

格式化字符串

1
2
3
4
%%py2js
"value: %f" % val

# 卧槽, 生成的代码太长了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
var _pyfunc_format = function (v, fmt) {  // nargs: 2
fmt = fmt.toLowerCase();
var s = String(v);
if (fmt.indexOf('!r') >= 0) {
try { s = JSON.stringify(v); } catch (e) { s = undefined; }
if (typeof s === 'undefined') { s = v._IS_COMPONENT ? v.id : String(v); }
}
var i0 = fmt.indexOf(':');
if (i0 < 0) {
} else if (fmt.indexOf('i', i0) > i0) { // integer formatting
s = Number.parseInt(v).toFixed(0);
} else if (fmt.indexOf('f', i0) > i0) { // float formatting
v = Number.parseFloat(v);
var spec = fmt.slice(i0+1, fmt.indexOf('f', i0));
var decimals = 6;
if (spec.indexOf('.') >= 0) {
var decimals = Number(spec.slice(spec.indexOf('.')+1));
}
s = v.toFixed(decimals);
} else if (fmt.indexOf('e', i0) > i0) { // exp formatting
v = Number.parseFloat(v);
var precision = 6;
var spec = fmt.slice(i0+1, fmt.indexOf('e', i0));
if (spec.indexOf('.') >= 0) {
precision = Number(spec.slice(spec.indexOf('.')+1)) || 1;
}
s = v.toExponential(precision);
} else if (fmt.indexOf('g', i0) > i0) { // "general" formatting
v = Number.parseFloat(v);
var precision = 6;
var spec = fmt.slice(i0+1, fmt.indexOf('g', i0));
if (spec.indexOf('.') >= 0) {
precision = Number(spec.slice(spec.indexOf('.')+1)) || 1;
}
// Exp or decimal?
s = v.toExponential(precision-1);
var s1 = s.slice(0, s.indexOf('e')), s2 = s.slice(s.indexOf('e'));
if (s2.length == 3) { s2 = 'e' + s2[1] + '0' + s2[2]; }
var exp = Number(s2.slice(1));
if (exp >= -4 && exp < precision) { s1=v.toPrecision(precision); s2=''; }
// Skip trailing zeros and dot
var j = s1.length-1;
while (j>0 && s1[j] == '0') { j-=1; }
s1 = s1.slice(0, j+1);
if (s1.endsWith('.')) { s1 = s1.slice(0, s1.length-1); }
s = s1 + s2;
}
if (i0 >= 0 && v > 0) {
if (fmt[i0+1] == '+') { s = '+' + s; }
if (fmt[i0+1] == ' ') { s = ' ' + s; }
}
return s;
};
var _pymeth_format = function () {
if (this.constructor !== String) return this.format.apply(this, arguments);
var parts = [], i = 0, i1, i2;
var itemnr = -1;
while (i < this.length) {
// find opening
i1 = this.indexOf('{', i);
if (i1 < 0 || i1 == this.length-1) { break; }
if (this[i1+1] == '{') {parts.push(this.slice(i, i1+1)); i = i1 + 2; continue;}
// find closing
i2 = this.indexOf('}', i1);
if (i2 < 0) { break; }
// parse
itemnr += 1;
var fmt = this.slice(i1+1, i2);
var index = fmt.split(':')[0].split('!')[0];
index = index? Number(index) : itemnr
var s = _pyfunc_format(arguments[index], fmt);
parts.push(this.slice(i, i1), s);
i = i2 + 1;
}
parts.push(this.slice(i));
return parts.join('');
};
_pymeth_format.call("value: {:f}", val);
1
2
3
%%py2js

'{a}'.format(a)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
var _pyfunc_format = function (v, fmt) {  // nargs: 2
fmt = fmt.toLowerCase();
var s = String(v);
if (fmt.indexOf('!r') >= 0) {
try { s = JSON.stringify(v); } catch (e) { s = undefined; }
if (typeof s === 'undefined') { s = v._IS_COMPONENT ? v.id : String(v); }
}
var i0 = fmt.indexOf(':');
if (i0 < 0) {
} else if (fmt.indexOf('i', i0) > i0) { // integer formatting
s = Number.parseInt(v).toFixed(0);
} else if (fmt.indexOf('f', i0) > i0) { // float formatting
v = Number.parseFloat(v);
var spec = fmt.slice(i0+1, fmt.indexOf('f', i0));
var decimals = 6;
if (spec.indexOf('.') >= 0) {
var decimals = Number(spec.slice(spec.indexOf('.')+1));
}
s = v.toFixed(decimals);
} else if (fmt.indexOf('e', i0) > i0) { // exp formatting
v = Number.parseFloat(v);
var precision = 6;
var spec = fmt.slice(i0+1, fmt.indexOf('e', i0));
if (spec.indexOf('.') >= 0) {
precision = Number(spec.slice(spec.indexOf('.')+1)) || 1;
}
s = v.toExponential(precision);
} else if (fmt.indexOf('g', i0) > i0) { // "general" formatting
v = Number.parseFloat(v);
var precision = 6;
var spec = fmt.slice(i0+1, fmt.indexOf('g', i0));
if (spec.indexOf('.') >= 0) {
precision = Number(spec.slice(spec.indexOf('.')+1)) || 1;
}
// Exp or decimal?
s = v.toExponential(precision-1);
var s1 = s.slice(0, s.indexOf('e')), s2 = s.slice(s.indexOf('e'));
if (s2.length == 3) { s2 = 'e' + s2[1] + '0' + s2[2]; }
var exp = Number(s2.slice(1));
if (exp >= -4 && exp < precision) { s1=v.toPrecision(precision); s2=''; }
// Skip trailing zeros and dot
var j = s1.length-1;
while (j>0 && s1[j] == '0') { j-=1; }
s1 = s1.slice(0, j+1);
if (s1.endsWith('.')) { s1 = s1.slice(0, s1.length-1); }
s = s1 + s2;
}
if (i0 >= 0 && v > 0) {
if (fmt[i0+1] == '+') { s = '+' + s; }
if (fmt[i0+1] == ' ') { s = ' ' + s; }
}
return s;
};
var _pymeth_format = function () {
if (this.constructor !== String) return this.format.apply(this, arguments);
var parts = [], i = 0, i1, i2;
var itemnr = -1;
while (i < this.length) {
// find opening
i1 = this.indexOf('{', i);
if (i1 < 0 || i1 == this.length-1) { break; }
if (this[i1+1] == '{') {parts.push(this.slice(i, i1+1)); i = i1 + 2; continue;}
// find closing
i2 = this.indexOf('}', i1);
if (i2 < 0) { break; }
// parse
itemnr += 1;
var fmt = this.slice(i1+1, i2);
var index = fmt.split(':')[0].split('!')[0];
index = index? Number(index) : itemnr
var s = _pyfunc_format(arguments[index], fmt);
parts.push(this.slice(i, i1), s);
i = i2 + 1;
}
parts.push(this.slice(i));
return parts.join('');
};
_pymeth_format.call("{a}", a);

赋值操作

1
2
3
%%py2js

bar.foo = 3
1
bar.foo = 3;
1
2
%%py2js
a = 1, 2, 3
1
2
var a;
a = [1, 2, 3];
1
2
%%py2js
a1, a2, a3 = a
1
2
3
var a1, a2, a3, stub1_;
stub1_ = a;
a1 = stub1_[0];a2 = stub1_[1];a3 = stub1_[2];
1
2
3
%%py2js

del bar.foo
1
delete bar.foo;
1
2
3
%%py2js
# 大写字母开头, 实例化这个类
foo = Foo()
1
2
var foo;
foo = new Foo();

比较

1
2
3
%%py2js

foo is bar
1
foo === bar;
1
2
3
%%py2js

foo == bar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var _pyfunc_op_equals = function op_equals (a, b) { // nargs: 2
if (a == null || b == null) {
} else if (Array.isArray(a) && Array.isArray(b)) {
var i = 0, iseq = a.length == b.length;
while (iseq && i < a.length) {iseq = op_equals(a[i], b[i]); i+=1;}
return iseq;
} else if (a.constructor === Object && b.constructor === Object) {
var akeys = Object.keys(a), bkeys = Object.keys(b);
akeys.sort(); bkeys.sort();
var i=0, k, iseq = op_equals(akeys, bkeys);
while (iseq && i < akeys.length)
{k=akeys[i]; iseq = op_equals(a[k], b[k]); i+=1;}
return iseq;
} return a == b;
};
_pyfunc_op_equals(foo, bar);
1
2
3
4
%%py2js
# 深度比较, 和js不同
(2, 3, 4) == (2, 3, 4)
(2, 3) in [(1,2), (2,3), (3,4)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var _pyfunc_op_contains = function op_contains (a, b) { // nargs: 2
if (b == null) {
} else if (Array.isArray(b)) {
for (var i=0; i<b.length; i++) {if (_pyfunc_op_equals(a, b[i]))
return true;}
return false;
} else if (b.constructor === Object) {
for (var k in b) {if (a == k) return true;}
return false;
} else if (b.constructor == String) {
return b.indexOf(a) >= 0;
} var e = Error('Not a container: ' + b); e.name='TypeError'; throw e;
};
var _pyfunc_op_equals = function op_equals (a, b) { // nargs: 2
if (a == null || b == null) {
} else if (Array.isArray(a) && Array.isArray(b)) {
var i = 0, iseq = a.length == b.length;
while (iseq && i < a.length) {iseq = op_equals(a[i], b[i]); i+=1;}
return iseq;
} else if (a.constructor === Object && b.constructor === Object) {
var akeys = Object.keys(a), bkeys = Object.keys(b);
akeys.sort(); bkeys.sort();
var i=0, k, iseq = op_equals(akeys, bkeys);
while (iseq && i < akeys.length)
{k=akeys[i]; iseq = op_equals(a[k], b[k]); i+=1;}
return iseq;
} return a == b;
};
_pyfunc_op_equals([2, 3, 4], [2, 3, 4]);
_pyfunc_op_contains([2, 3], ([[1, 2], [2, 3], [3, 4]]));
1
2
3
%%py2js

foo is undefined
1
foo === undefined;
1
2
3
%%py2js

"foo" in "this has foo in it"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var _pyfunc_op_contains = function op_contains (a, b) { // nargs: 2
if (b == null) {
} else if (Array.isArray(b)) {
for (var i=0; i<b.length; i++) {if (_pyfunc_op_equals(a, b[i]))
return true;}
return false;
} else if (b.constructor === Object) {
for (var k in b) {if (a == k) return true;}
return false;
} else if (b.constructor == String) {
return b.indexOf(a) >= 0;
} var e = Error('Not a container: ' + b); e.name='TypeError'; throw e;
};
var _pyfunc_op_equals = function op_equals (a, b) { // nargs: 2
if (a == null || b == null) {
} else if (Array.isArray(a) && Array.isArray(b)) {
var i = 0, iseq = a.length == b.length;
while (iseq && i < a.length) {iseq = op_equals(a[i], b[i]); i+=1;}
return iseq;
} else if (a.constructor === Object && b.constructor === Object) {
var akeys = Object.keys(a), bkeys = Object.keys(b);
akeys.sort(); bkeys.sort();
var i=0, k, iseq = op_equals(akeys, bkeys);
while (iseq && i < akeys.length)
{k=akeys[i]; iseq = op_equals(a[k], b[k]); i+=1;}
return iseq;
} return a == b;
};
_pyfunc_op_contains("foo", "this has foo in it");
1
2
3
%%py2js

3 in [0, 1, 2, 3, 4]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var _pyfunc_op_contains = function op_contains (a, b) { // nargs: 2
if (b == null) {
} else if (Array.isArray(b)) {
for (var i=0; i<b.length; i++) {if (_pyfunc_op_equals(a, b[i]))
return true;}
return false;
} else if (b.constructor === Object) {
for (var k in b) {if (a == k) return true;}
return false;
} else if (b.constructor == String) {
return b.indexOf(a) >= 0;
} var e = Error('Not a container: ' + b); e.name='TypeError'; throw e;
};
var _pyfunc_op_equals = function op_equals (a, b) { // nargs: 2
if (a == null || b == null) {
} else if (Array.isArray(a) && Array.isArray(b)) {
var i = 0, iseq = a.length == b.length;
while (iseq && i < a.length) {iseq = op_equals(a[i], b[i]); i+=1;}
return iseq;
} else if (a.constructor === Object && b.constructor === Object) {
var akeys = Object.keys(a), bkeys = Object.keys(b);
akeys.sort(); bkeys.sort();
var i=0, k, iseq = op_equals(akeys, bkeys);
while (iseq && i < akeys.length)
{k=akeys[i]; iseq = op_equals(a[k], b[k]); i+=1;}
return iseq;
} return a == b;
};
_pyfunc_op_contains(3, [0, 1, 2, 3, 4]);

Truthy和Falsy

1
2
3
4
5
6
7
8
9
10
11
12
%%py2js

# 这些值都被判定为false

# These evaluate to False:
0
NaN
"" # empty string
None # JS null
undefined
[]
{}
1
2
3
4
5
6
7
0;
NaN;
"";
null;
undefined;
[];
({});
1
2
3
4
5
%%py2js

# 支持这样的操作
a = []
a = a or [1] # a is now [1]
1
2
3
4
5
6
7
8
9
10
var _pyfunc_truthy = function (v) {
if (v === null || typeof v !== "object") {return v;}
else if (v.length !== undefined) {return v.length ? v : false;}
else if (v.byteLength !== undefined) {return v.byteLength ? v : false;}
else if (v.constructor !== Object) {return true;}
else {return Object.getOwnPropertyNames(v).length ? v : false;}
};
var a;
a = [];
a = _pyfunc_truthy(a) || [1];

函数操作

1
2
3
4
%%py2js

# Business as usual
foo(a, b)
1
foo(a, b);
1
2
3
4
%%py2js

# 支持 args (不支持 **kwargs)
foo(*a)
1
foo.apply(null, a);

if语句

1
2
3
4
5
6
7
8
9
%%py2js


if val > 7:
result = 42
elif val > 1:
result = 1
else:
result = 0
1
2
3
4
5
6
7
8
var result;
if ((val > 7)) {
result = 42;
} else if ((val > 1)) {
result = 1;
} else {
result = 0;
}

循环

1
2
3
4
5
%%py2js

val = 0
while val < 10:
val += 1
1
2
3
4
5
6
7
8
9
10
var _pyfunc_op_add = function (a, b) { // nargs: 2
if (Array.isArray(a) && Array.isArray(b)) {
return a.concat(b);
} return a + b;
};
var val;
val = 0;
while (val < 10) {
val = _pyfunc_op_add(val, 1);
}
1
2
3
4
5
%%py2js

# Using range() yields true for-loops
for i in range(10):
print(i)
1
2
3
4
var i;
for (i = 0; i < 10; i += 1) {
console.log(i);
}
1
2
3
4
%%py2js

for i in range(100, 10, -2):
print(i)
1
2
3
4
var i;
for (i = 100; i > 10; i += -2) {
console.log(i);
}
1
2
3
4
5
%%py2js

# One way to iterate over an array
for i in range(len(arr)):
print(arr[i])
1
2
3
4
var i;
for (i = 0; i < arr.length; i += 1) {
console.log(arr[i]);
}
1
2
3
4
%%py2js
# But this is equally valid (and fast)
for element in arr:
print(element)
1
2
3
4
5
6
7
var element, stub1_seq, stub2_itr;
stub1_seq = arr;
if ((typeof stub1_seq === "object") && (!Array.isArray(stub1_seq))) { stub1_seq = Object.keys(stub1_seq);}
for (stub2_itr = 0; stub2_itr < stub1_seq.length; stub2_itr += 1) {
element = stub1_seq[stub2_itr];
console.log(element);
}
1
2
3
4
5
%%py2js

# 遍历字符串
for char in "foo bar":
print(c)
1
2
3
4
5
6
7
var char, stub1_seq, stub2_itr;
stub1_seq = "foo bar";
if ((typeof stub1_seq === "object") && (!Array.isArray(stub1_seq))) { stub1_seq = Object.keys(stub1_seq);}
for (stub2_itr = 0; stub2_itr < stub1_seq.length; stub2_itr += 1) {
char = stub1_seq[stub2_itr];
console.log(c);
}
1
2
3
4
5
%%py2js

# 更复杂的遍历
for i, j in [[1, 2], [3, 4]]:
print(i+j)
1
2
3
4
5
6
7
8
9
10
11
12
13
var _pyfunc_op_add = function (a, b) { // nargs: 2
if (Array.isArray(a) && Array.isArray(b)) {
return a.concat(b);
} return a + b;
};
var i, j, stub1_seq, stub2_itr, stub3_tgt;
stub1_seq = [[1, 2], [3, 4]];
if ((typeof stub1_seq === "object") && (!Array.isArray(stub1_seq))) { stub1_seq = Object.keys(stub1_seq);}
for (stub2_itr = 0; stub2_itr < stub1_seq.length; stub2_itr += 1) {
stub3_tgt = stub1_seq[stub2_itr];
i = stub3_tgt[0]; j = stub3_tgt[1];
console.log(_pyfunc_op_add(i, j));
}

支持用于迭代的Buildin函数:enumerate, zip, reversed, sorted, filter, map

1
2
3
4
%%py2js

for i, x in enumerate(foo):
print(i, x)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var _pyfunc_enumerate = function (iter) { // nargs: 1
var i, res=[];
if ((typeof iter==="object") && (!Array.isArray(iter))) {iter = Object.keys(iter);}
for (i=0; i<iter.length; i++) {res.push([i, iter[i]]);}
return res;
};
var i, stub1_seq, stub2_itr, stub3_tgt, x;
stub1_seq = _pyfunc_enumerate(foo);
if ((typeof stub1_seq === "object") && (!Array.isArray(stub1_seq))) { stub1_seq = Object.keys(stub1_seq);}
for (stub2_itr = 0; stub2_itr < stub1_seq.length; stub2_itr += 1) {
stub3_tgt = stub1_seq[stub2_itr];
i = stub3_tgt[0]; x = stub3_tgt[1];
console.log(i + " " + x);
}
1
2
3
4
%%py2js

for a, b in zip(foo, bar):
pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var _pyfunc_zip = function () { // nargs: 2 3 4 5 6 7 8 9
var i, j, tup, arg, args = [], res = [], len = 1e20;
for (i=0; i<arguments.length; i++) {
arg = arguments[i];
if ((typeof arg==="object") && (!Array.isArray(arg))) {arg = Object.keys(arg);}
args.push(arg);
len = Math.min(len, arg.length);
}
for (j=0; j<len; j++) {
tup = []
for (i=0; i<args.length; i++) {tup.push(args[i][j]);}
res.push(tup);
}
return res;
};
var a, b, stub1_seq, stub2_itr, stub3_tgt;
stub1_seq = _pyfunc_zip(foo, bar);
if ((typeof stub1_seq === "object") && (!Array.isArray(stub1_seq))) { stub1_seq = Object.keys(stub1_seq);}
for (stub2_itr = 0; stub2_itr < stub1_seq.length; stub2_itr += 1) {
stub3_tgt = stub1_seq[stub2_itr];
a = stub3_tgt[0]; b = stub3_tgt[1];
}
1
2
3
4
%%py2js

for x in reversed(sorted(foo)):
pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var _pyfunc_reversed = function (iter) { // nargs: 1
if ((typeof iter==="object") && (!Array.isArray(iter))) {iter = Object.keys(iter);}
return iter.slice().reverse();
};
var _pyfunc_sorted = function (iter, key, reverse) { // nargs: 1 2 3
if ((typeof iter==="object") && (!Array.isArray(iter))) {iter = Object.keys(iter);}
var comp = function (a, b) {a = key(a); b = key(b);
if (a<b) {return -1;} if (a>b) {return 1;} return 0;};
comp = Boolean(key) ? comp : undefined;
iter = iter.slice().sort(comp);
if (reverse) iter.reverse();
return iter;
};
var stub1_seq, stub2_itr, x;
stub1_seq = _pyfunc_reversed(_pyfunc_sorted(foo, undefined, false));
if ((typeof stub1_seq === "object") && (!Array.isArray(stub1_seq))) { stub1_seq = Object.keys(stub1_seq);}
for (stub2_itr = 0; stub2_itr < stub1_seq.length; stub2_itr += 1) {
x = stub1_seq[stub2_itr];
}
1
2
3
4
%%py2js

for x in map(lambda x: x+1, foo):
pass
1
2
3
4
5
6
7
8
9
10
11
var _pyfunc_map = function (func, iter) { // nargs: 2
if (typeof func === "undefined" || func === null) {func = function(x) {return x;}}
if ((typeof iter==="object") && (!Array.isArray(iter))) {iter = Object.keys(iter);}
return iter.map(func);
};
var stub1_seq, stub2_itr, x;
stub1_seq = _pyfunc_map((function (x) {return x + 1;}), foo);
if ((typeof stub1_seq === "object") && (!Array.isArray(stub1_seq))) { stub1_seq = Object.keys(stub1_seq);}
for (stub2_itr = 0; stub2_itr < stub1_seq.length; stub2_itr += 1) {
x = stub1_seq[stub2_itr];
}
1
2
3
4
%%py2js

for x in filter(lambda x: x>0, foo):
pass
1
2
3
4
5
6
7
8
9
10
11
var _pyfunc_filter = function (func, iter) { // nargs: 2
if (typeof func === "undefined" || func === null) {func = function(x) {return x;}}
if ((typeof iter==="object") && (!Array.isArray(iter))) {iter = Object.keys(iter);}
return iter.filter(func);
};
var stub1_seq, stub2_itr, x;
stub1_seq = _pyfunc_filter((function (x) {return x > 0;}), foo);
if ((typeof stub1_seq === "object") && (!Array.isArray(stub1_seq))) { stub1_seq = Object.keys(stub1_seq);}
for (stub2_itr = 0; stub2_itr < stub1_seq.length; stub2_itr += 1) {
x = stub1_seq[stub2_itr];
}

list 推导

1
2
3
4
5
%%py2js

# List comprehensions just work
x = [i*2 for i in some_array if i>0]
y = [i*j for i in a for j in b]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var _pyfunc_op_mult = function (a, b) { // nargs: 2
if ((typeof a === 'number') + (typeof b === 'number') === 1) {
if (a.constructor === String) return _pymeth_repeat.call(a, b);
if (b.constructor === String) return _pymeth_repeat.call(b, a);
if (Array.isArray(b)) {var t=a; a=b; b=t;}
if (Array.isArray(a)) {
var res = []; for (var i=0; i<b; i++) res = res.concat(a);
return res;
}
} return a * b;
};
var _pymeth_repeat = function(count) { // nargs: 0
if (this.repeat) return this.repeat(count);
if (count < 1) return '';
var result = '', pattern = this.valueOf();
while (count > 1) {
if (count & 1) result += pattern;
count >>= 1, pattern += pattern;
}
return result + pattern;
};
var stub1_, stub1_i, stub1_i0, stub1_iter0, stub2_, stub2_i, stub2_i0, stub2_i1, stub2_iter0, stub2_iter1, stub2_j, x, y;
stub1_ = [];stub1_iter0 = some_array;if ((typeof stub1_iter0 === "object") && (!Array.isArray(stub1_iter0))) {stub1_iter0 = Object.keys(stub1_iter0);}for (stub1_i0=0; stub1_i0<stub1_iter0.length; stub1_i0++) {stub1_i = stub1_iter0[stub1_i0];if (!((stub1_i > 0))) {continue;}{stub1_.push(_pyfunc_op_mult(stub1_i, 2));}}
x = stub1_;
stub2_ = [];stub2_iter0 = a;if ((typeof stub2_iter0 === "object") && (!Array.isArray(stub2_iter0))) {stub2_iter0 = Object.keys(stub2_iter0);}for (stub2_i0=0; stub2_i0<stub2_iter0.length; stub2_i0++) {stub2_i = stub2_iter0[stub2_i0];stub2_iter1 = b;if ((typeof stub2_iter1 === "object") && (!Array.isArray(stub2_iter1))) {stub2_iter1 = Object.keys(stub2_iter1);}for (stub2_i1=0; stub2_i1<stub2_iter1.length; stub2_i1++) {stub2_j = stub2_iter1[stub2_i1];{stub2_.push(_pyfunc_op_mult(stub2_i, stub2_j));}}}
y = stub2_;

定义函数

1
2
3
4
%%py2js

def display(val):
print(val)
1
2
3
4
5
6
var display;
display = function flx_display (val) {
console.log(val);
return null;
};

1
2
3
4
5
%%py2js

# 支持 *args
def foo(x, *values):
bar(x+1, *values)
1
2
3
4
5
6
7
8
var foo;
foo = function flx_foo (x) {
var values;
values = Array.prototype.slice.call(arguments, 1);
bar.apply(null, [].concat([x + 1], values));
return null;
};

1
2
3
4
5
6
7
8
%%py2js

# 想要混编JavaScript, 也可以使用RawJS
def bar(a, b):
RawJS('''
var c = 4;
return a + b + c;
''')
1
2
3
4
5
6
7
var bar;
bar = function flx_bar (a, b) {
var c = 4;
return a + b + c;;
return null;
};

1
2
3
4
%%py2js

# Lambda 表达式
foo = lambda x: x**2
1
2
var foo;
foo = function (x) {return Math.pow(x, 2);};

定义类

类被转换为JavaScript原型类paragigm,这意味着它们应该可以很好地与其他JS库一起使用。支持继承,但不支持多重继承。此外,super()的工作方式与Python 3相同。

1
2
3
4
5
%%py2js
class Foo:
a_class_attribute = 4
def __init__(self):
self.x = 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var _pyfunc_op_instantiate = function (ob, args) { // nargs: 2
if ((typeof ob === "undefined") ||
(typeof window !== "undefined" && window === ob) ||
(typeof global !== "undefined" && global === ob))
{throw "Class constructor is called as a function.";}
for (var name in ob) {
if (Object[name] === undefined &&
typeof ob[name] === 'function' && !ob[name].nobind) {
ob[name] = ob[name].bind(ob);
ob[name].__name__ = name;
}
}
if (ob.__init__) {
ob.__init__.apply(ob, args);
}
};
var Foo;
Foo = function () {
_pyfunc_op_instantiate(this, arguments);
}
Foo.prototype._base_class = Object;
Foo.prototype.__name__ = "Foo";

Foo.prototype.a_class_attribute = 4;
Foo.prototype.__init__ = function () {
this.x = 3;
return null;
};


1
2
3
4
5
6
7
8
%%py2js

class Bar(Foo):
def __init__(self):
super.__init__()
self.x += 1
def add1(self):
self.x += 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var _pyfunc_op_add = function (a, b) { // nargs: 2
if (Array.isArray(a) && Array.isArray(b)) {
return a.concat(b);
} return a + b;
};
var _pyfunc_op_instantiate = function (ob, args) { // nargs: 2
if ((typeof ob === "undefined") ||
(typeof window !== "undefined" && window === ob) ||
(typeof global !== "undefined" && global === ob))
{throw "Class constructor is called as a function.";}
for (var name in ob) {
if (Object[name] === undefined &&
typeof ob[name] === 'function' && !ob[name].nobind) {
ob[name] = ob[name].bind(ob);
ob[name].__name__ = name;
}
}
if (ob.__init__) {
ob.__init__.apply(ob, args);
}
};
var Bar;
Bar = function () {
_pyfunc_op_instantiate(this, arguments);
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype._base_class = Foo.prototype;
Bar.prototype.__name__ = "Bar";

Bar.prototype.__init__ = function () {
super.__init__();
this.x = _pyfunc_op_add(this.x, 1);
return null;
};

Bar.prototype.add1 = function () {
this.x = _pyfunc_op_add(this.x, 1);
return null;
};


1
2
3
4
5
%%py2js

# 实例化和调用方法
b = Bar()
setTimeout(b.add1, 1000)
1
2
3
var b;
b = new Bar();
setTimeout(b.add1, 1000);
1
2
3
4
5
6
%%py2js
# Functions defined in methods (and that do not start with self or this)
# have ``this`` bound the the same object.
class Spam(Bar):
def add_later(self):
setTimeout(lambda ev: self.add1(), 1000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var _pyfunc_op_instantiate = function (ob, args) { // nargs: 2
if ((typeof ob === "undefined") ||
(typeof window !== "undefined" && window === ob) ||
(typeof global !== "undefined" && global === ob))
{throw "Class constructor is called as a function.";}
for (var name in ob) {
if (Object[name] === undefined &&
typeof ob[name] === 'function' && !ob[name].nobind) {
ob[name] = ob[name].bind(ob);
ob[name].__name__ = name;
}
}
if (ob.__init__) {
ob.__init__.apply(ob, args);
}
};
var Spam;
Spam = function () {
_pyfunc_op_instantiate(this, arguments);
}
Spam.prototype = Object.create(Bar.prototype);
Spam.prototype._base_class = Bar.prototype;
Spam.prototype.__name__ = "Spam";

Spam.prototype.add_later = function () {
setTimeout((function (ev) {return this.add1();}).bind(this), 1000);
return null;
};


异常

引发的异常将转换为JavaScript Error对象,其name属性设置为引发的异常类型。捕获异常时,将检查name属性(如果它是一个Error对象)。您可以raise字符串或任何其他类型的对象,但您只能捕获Error对象。

1
2
3
4
5
6
7
%%py2js

# 抛出异常
raise SomeError('asd')
raise AnotherError()
raise "In JS you can throw anything"
raise 4
1
2
3
4
5
6
7
8
9
10
var _pyfunc_op_error = function (etype, msg) { // nargs: 2
var e = new Error(etype + ': ' + msg);
e.name = etype
return e;
};
var err_0;
throw _pyfunc_op_error('SomeError', "asd");
throw _pyfunc_op_error('AnotherError', "");
throw "In JS you can throw anything";
throw 4;
1
2
3
4
%%py2js
# Assertions 能正常工作
assert foo == 3
assert bar == 4, "bar should be 4"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var _pyfunc_op_equals = function op_equals (a, b) { // nargs: 2
if (a == null || b == null) {
} else if (Array.isArray(a) && Array.isArray(b)) {
var i = 0, iseq = a.length == b.length;
while (iseq && i < a.length) {iseq = op_equals(a[i], b[i]); i+=1;}
return iseq;
} else if (a.constructor === Object && b.constructor === Object) {
var akeys = Object.keys(a), bkeys = Object.keys(b);
akeys.sort(); bkeys.sort();
var i=0, k, iseq = op_equals(akeys, bkeys);
while (iseq && i < akeys.length)
{k=akeys[i]; iseq = op_equals(a[k], b[k]); i+=1;}
return iseq;
} return a == b;
};
var _pyfunc_op_error = function (etype, msg) { // nargs: 2
var e = new Error(etype + ': ' + msg);
e.name = etype
return e;
};
if (!(_pyfunc_op_equals(foo, 3))) { throw _pyfunc_op_error('AssertionError', "_pyfunc_op_equals(foo, 3)");}
if (!(_pyfunc_op_equals(bar, 4))) { throw _pyfunc_op_error('AssertionError', "\"bar should be 4\"");}
1
2
3
4
5
6
7
8
9
%%py2js

# 捕捉异常
try:
raise IndexError('blabla')
except IndexError as err:
print(err)
except Exception:
print('something went wrong')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var _pyfunc_op_error = function (etype, msg) { // nargs: 2
var e = new Error(etype + ': ' + msg);
e.name = etype
return e;
};
var err, err_1;
try {
throw _pyfunc_op_error('IndexError', "blabla");
} catch(err_1) {
if (err_1 instanceof Error && err_1.name === "IndexError") {
err = err_1;
console.log(err);
} else {
console.log("something went wrong");
}
}

python内置函数

大多数buildin函数(在JS中有意义的)会自动转换为JavaScript的内建函数:isinstance,issubclass,callable,hasattr,getattr,setattr,delattr,print,len,max,min,chr,ord,dict,list,tuple,range, pow,sum,round,int,float,str,bool,abs,divmod,all,any,enumerate,zip,reversed,sorted,filtered,map。

此外,还实现了list,dict和str的所有方法(str方法除外:encode,decode,format,format_map,isdecimal,isdigit,isprintable,maketrans)。

1
2
3
4
%%py2js
# Printing 能正常工作
print('some test')
print(a, b, c, sep='-')
1
2
console.log("some test");
console.log(a + "-" + b + "-" + c);
1
2
3
%%py2js
# 获取字符串长度
len(foo)
1
foo.length;
1
2
3
4
5
%%py2js
# 保留小数和绝对值
round(foo) # round to nearest integer
int(foo) # round towards 0 as in Python
abs(foo)
1
2
3
4
5
6
7
8
var _pyfunc_abs = Math.abs;
var _pyfunc_int = function (x) { // nargs: 1
return x<0 ? Math.ceil(x): Math.floor(x);
};
var _pyfunc_round = Math.round;
_pyfunc_round(foo);
_pyfunc_int(foo);
_pyfunc_abs(foo);
1
2
3
4
5
6
%%py2js
# 最大值和最小值
min(foo)
min(a, b, c)
max(foo)
max(a, b, c)
1
2
3
4
Math.min.apply(null, foo);
Math.min(a, b, c);
Math.max.apply(null, foo);
Math.max(a, b, c);
1
2
3
%%py2js
# 求余数
a, b = divmod(100, 7) # -> 14, 2
1
2
3
4
5
6
var _pyfunc_divmod = function (x, y) { // nargs: 2
var m = x % y; return [(x-m)/y, m];
};
var a, b, stub1_;
stub1_ = _pyfunc_divmod(100, 7);
a = stub1_[0];b = stub1_[1];
1
2
3
4
5
%%py2js
# 聚合
sum(foo)
all(foo)
any(foo)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var _pyfunc_all = function (x) { // nargs: 1
for (var i=0; i<x.length; i++) {
if (!_pyfunc_truthy(x[i])){return false;}
} return true;
};
var _pyfunc_any = function (x) { // nargs: 1
for (var i=0; i<x.length; i++) {
if (_pyfunc_truthy(x[i])){return true;}
} return false;
};
var _pyfunc_sum = function (x) { // nargs: 1
return x.reduce(function(a, b) {return a + b;});
};
var _pyfunc_truthy = function (v) {
if (v === null || typeof v !== "object") {return v;}
else if (v.length !== undefined) {return v.length ? v : false;}
else if (v.byteLength !== undefined) {return v.byteLength ? v : false;}
else if (v.constructor !== Object) {return true;}
else {return Object.getOwnPropertyNames(v).length ? v : false;}
};
_pyfunc_sum(foo);
_pyfunc_all(foo);
_pyfunc_any(foo);
1
2
3
4
5
6
7
8
%%py2js
# 数据转换: numbers, bools and strings
str(s)
float(x)
bool(y)
int(z) # this rounds towards zero like in Python
chr(65) # -> 'A'
ord('A') # -> 65
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var _pyfunc_bool = function (x) { // nargs: 1
return Boolean(_pyfunc_truthy(x));
};
var _pyfunc_float = Number;
var _pyfunc_int = function (x) { // nargs: 1
return x<0 ? Math.ceil(x): Math.floor(x);
};
var _pyfunc_str = String;
var _pyfunc_truthy = function (v) {
if (v === null || typeof v !== "object") {return v;}
else if (v.length !== undefined) {return v.length ? v : false;}
else if (v.byteLength !== undefined) {return v.byteLength ? v : false;}
else if (v.constructor !== Object) {return true;}
else {return Object.getOwnPropertyNames(v).length ? v : false;}
};
_pyfunc_str(s);
_pyfunc_float(x);
_pyfunc_bool(y);
_pyfunc_int(z);
String.fromCharCode(65);
"A".charCodeAt(0);
1
2
3
4
5
6
%%py2js
# 数据转换成 lists and dicts
dict([['foo', 1], ['bar', 2]]) # -> {'foo': 1, 'bar': 2}
list('abc') # -> ['a', 'b', 'c']
dict(other_dict) # make a copy
list(other_list) # make copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var _pyfunc_dict = function (x) {
var t, i, keys, r={};
if (Array.isArray(x)) {
for (i=0; i<x.length; i++) {
t=x[i]; r[t[0]] = t[1];
}
} else {
keys = Object.keys(x);
for (i=0; i<keys.length; i++) {
t=keys[i]; r[t] = x[t];
}
}
return r;
};
var _pyfunc_list = function (x) {
var r=[];
if (typeof x==="object" && !Array.isArray(x)) {x = Object.keys(x)}
for (var i=0; i<x.length; i++) {
r.push(x[i]);
}
return r;
};
_pyfunc_dict(([["foo", 1], ["bar", 2]]));
_pyfunc_list("abc");
_pyfunc_dict(other_dict);
_pyfunc_list(other_list);

isinstance相关功能

1
2
3
4
5
6
7
%%py2js
# 基本类型
isinstance(3, float) # in JS there are no ints
isinstance('', str)
isinstance([], list)
isinstance({}, dict)
isinstance(foo, types.FunctionType)
1
2
3
4
5
Object.prototype.toString.call(3).slice(8,-1).toLowerCase() === 'number';
Object.prototype.toString.call("").slice(8,-1).toLowerCase() === 'string';
Array.isArray([]);
Object.prototype.toString.call(({})).slice(8,-1).toLowerCase() === 'object';
Object.prototype.toString.call(foo).slice(8,-1).toLowerCase() === 'function';
1
2
3
4
5
6
7
%%py2js
# 也可以用JS的字符串
isinstance(3, 'number')
isinstance('', 'string')
isinstance([], 'array')
isinstance({}, 'object')
isinstance(foo, 'function')
1
2
3
4
5
Object.prototype.toString.call(3).slice(8,-1).toLowerCase() === 'number';
Object.prototype.toString.call("").slice(8,-1).toLowerCase() === 'string';
Array.isArray([]);
Object.prototype.toString.call(({})).slice(8,-1).toLowerCase() === 'object';
Object.prototype.toString.call(foo).slice(8,-1).toLowerCase() === 'function';
1
2
3
4
5
%%py2js
# 可以用于自定义类型.
isinstance(x, MyClass)
isinstance(x, 'MyClass') # equivalent
isinstance(x, 'Object') # also yields true (subclass of Object)
1
2
3
x instanceof MyClass;
x instanceof MyClass;
Object.prototype.toString.call(x).slice(8,-1).toLowerCase() === 'object';
1
2
3
%%py2js
# issubclass 也能正常工作
issubclass(Foo, Bar)
1
(Foo.prototype instanceof Bar);
1
2
3
%%py2js
# callable也能正常工作
callable(foo)
1
(typeof foo === "function");

hasattr, getattr, setattr and delattr

1
2
3
4
5
6
7
8
9
10
%%py2js

a = {'foo': 1, 'bar': 2}
hasattr(a, 'foo') # -> True
hasattr(a, 'fooo') # -> False
hasattr(null, 'foo') # -> False
getattr(a, 'foo') # -> 1
getattr(a, 'fooo') # -> raise AttributeError
getattr(a, 'fooo', 3) # -> 3
getattr(null, 'foo', 3) # -> 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var _pyfunc_getattr = function (ob, name, deflt) { // nargs: 2 3
var has_attr = ob !== undefined && ob !== null && ob[name] !== undefined;
if (has_attr) {return ob[name];}
else if (arguments.length == 3) {return deflt;}
else {var e = Error(name); e.name='AttributeError'; throw e;}
};
var _pyfunc_hasattr = function (ob, name) { // nargs: 2
return (ob !== undefined) && (ob !== null) && (ob[name] !== undefined);
};
var a;
a = ({foo: 1, bar: 2});
_pyfunc_hasattr(a, "foo");
_pyfunc_hasattr(a, "fooo");
_pyfunc_hasattr(null, "foo");
_pyfunc_getattr(a, "foo");
_pyfunc_getattr(a, "fooo");
_pyfunc_getattr(a, "fooo", 3);
_pyfunc_getattr(null, "foo", 3);
1
2
3
%%py2js

setattr(a, 'foo', 2)
1
2
3
4
var _pyfunc_setattr = function (ob, name, value) {  // nargs: 3
ob[name] = value;
};
_pyfunc_setattr(a, "foo", 2);
1
2
3
%%py2js

delattr(a, 'foo')
1
2
3
4
var _pyfunc_delattr = function (ob, name) {  // nargs: 2
delete ob[name];
};
_pyfunc_delattr(a, "foo");

使用JS专属功能

在Python模块中编写PyScript时,我们建议在使用特定JavaScript功能的地方,引用以window为前缀。其中window表示全局JS命名空间。所有全局JavaScript对象/函数和变量都自动成为window对象的成员。这有助于明确该功能是JS特有的,还有助于像flake8这样的静态代码分析工具。

除了window, flexx.pyscript 也提供 undefined, Inifinity, 和 NaN.

1
2
3
4
%%py2js

def foo(a):
return window.Math.cos(a)
1
2
3
4
5
var foo;
foo = function flx_foo (a) {
return window.Math.cos(a);
};

注意
本文由jupyter notebook转换而来, 您可以在这里下载notebook
统计咨询请加QQ 2726725926, 微信 mllncn, SPSS统计咨询是收费的
微博上@mlln-cn可以向我免费题问
请记住我的网址: mlln.cn 或者 jupyter.cn