显然 PyV8 没有正确地将 python 列表转换为 Javascript 数组,这导致my_list.length
returnundefined
被转换为 None。
ctx = PyV8.JSContext()
ctx.enter()
ctx.locals.a = [{'a':1}]
print ctx.locals.a
#> [{'a': 1}]
print ctx.eval("a.length")
#> None
print ctx.eval("a[0].a")
#> 1
ctx.locals.blub = {'a':1}
print ctx.eval("blub.a")
#> 1
print ctx.eval("Object.keys(blub)")
#> a
ctx.locals.blub = {'a':[1,2,3]}
print ctx.eval("Object.keys(blub)")
#> a
print ctx.eval("blub.a")
#> [1, 2, 3]
ctx.locals.blub2 = [{'a':[1,2,3]}]
print ctx.eval("blub2")
#> [{'a': [1, 2, 3]}]
print ctx.eval("blub2.length")
#> None
print ctx.eval("Array.isArray(blub2)")
#> False
print ctx.eval("typeof(blub2)")
#> object
print ctx.eval("blub2[0].a")
#> [1, 2, 3]
print ctx.eval("typeof(blub.a)")
#> object
print ctx.eval("Array.isArray(blub.a)")
#> False
答案是使用PyV8.JSArray(my_list)
. 我为我的项目编写了以下帮助函数,这些函数可以处理各种小问题,并且可以轻松地在 python 和 js 对象之间来回转换。然而,这些针对的是 PyV8 的特定版本(这是我可以推荐的唯一版本,请参阅链接问题中的讨论),因此如果您按原样使用它们,您的结果可能会有所不同。示例用法:
ctx.locals.blub3 = get_js_obj({'a':[1,2,3]})
ctx.locals.blub4 = get_js_obj([1,2,3])
ctx.eval("blub3.a.length")
#> 3
ctx.eval("blub4.length")
#> 3
这是功能。
def access_with_js(ctx, route):
if len(route) == 0:
raise Exception("route must have at least one element")
accessor_string = route[0]
for elem in route[1:]:
if type(elem) in [str, unicode]:
accessor_string += "['" + elem + "']"
elif type(elem) == int:
accessor_string += "[" + str(elem) + "]"
else:
raise Exception("invalid element in route, must be text or number")
return ctx.eval(accessor_string)
def get_py_obj(ctx, obj, route=[]):
def dict_is_empty(dict):
for key in dict:
return False
return True
def access(obj, key):
if key in obj:
return obj[key]
return None
cloned = None
if isinstance(obj, list) or isinstance(obj, PyV8.JSArray):
cloned = []
temp = str(access_with_js(ctx, route)) #working around a problem with PyV8 r429
num_elements = len(obj)
for index in range(num_elements):
elem = obj[index]
cloned.append(get_py_obj(ctx, elem, route + [index]))
elif isinstance(obj, dict) or isinstance(obj, PyV8.JSObject):
cloned = {}
for key in obj.keys():
cloned_val = None
if type(key) == int:
#workaround for a problem with PyV8 where it won't let me access
#objects with integer accessors
val = None
try:
val = access(obj, str(key))
except KeyError:
pass
if val == None:
val = access(obj, key)
cloned_val = get_py_obj(ctx, val, route + [key])
else:
cloned_val = get_py_obj(ctx, access(obj, key), route + [key])
cloned[key] = cloned_val
elif type(obj) == str:
cloned = obj.decode('utf-8')
else:
cloned = obj
return cloned
def get_js_obj(ctx,obj):
#workaround for a problem with PyV8 where it will implicitely convert python lists to js objects
#-> we need to explicitely do the conversion. see also the wrapper classes for JSContext above.
if isinstance(obj, list):
js_list = []
for entry in obj:
js_list.append(get_js_obj(ctx,entry))
return PyV8.JSArray(js_list)
elif isinstance(obj, dict):
js_obj = ctx.eval("new Object();") # PyV8.JSObject cannot be instantiated from Python
for key in obj.keys():
try:
js_obj[key] = get_js_obj(ctx,obj[key])
except Exception, e:
# unicode keys raise a Boost.Python.ArgumentError
# which can't be caught directly:
# https://mail.python.org/pipermail/cplusplus-sig/2010-April/015470.html
if (not str(e).startswith("Python argument types in")):
raise
import unicodedata
js_obj[unicodedata.normalize('NFKD', key).encode('ascii','ignore')] = get_js_obj(ctx,obj[key])
return js_obj
else:
return obj