
编程语言语言运行时编译器前端【免费下载链接】brythonBrython (Browser Python) is an implementation of Python 3 running in the browser项目地址https://gitcode.com/gh_mirrors/br/brython点击查看免费下载本文以 BrythonBrowser Python官方文档 www/doc/en/jsobjects.md 为骨架系统讲解 Python 程序与 JavaScript 程序在同一 HTML 页面中如何双向调用既要从 Brython 脚本中使用 Javascript 数据全局变量、函数、构造函数、jQuery 等库也要让 JavaScript 程序反过来访问 Brython 定义的模块与对象。读完本文你将掌握类型自动转换规则、new构造函数调用、JavascriptError异常捕获、__BRYTHON__运行时 APIwhenReady、getPythonModule、runPythonSource等的完整用法并理解其底层实现位置。背景Python 与 JavaScript 共存于同一页面Brython 的核心定位是在浏览器中运行 Python 3因此一个 HTML 文档完全可以同时包含 JavaScript 脚本与 Python 脚本。两种脚本的执行时机不同这是理解所有互操作问题的关键JavaScript 脚本在页面加载过程中边加载边执行on the flyBrython 脚本在 HTML 完全加载完成之后才执行。因此在页面完全加载完成之前JavaScript 程序无法使用 Brython 数据反过来Brython 脚本运行时页面通常已就绪可以直接访问 JavaScript 添加到全局命名空间的名字。这一时序差异决定了__BRYTHON__.whenReady这类 API 的必要性详见下文。下文按官方文档的两个方向组织从 Brython 程序使用 JavaScript 数据从 JavaScript 程序使用 Brython 数据。从 Brython 程序使用 JavaScript 数据通过window对象访问 JS 全局命名空间JavaScript 程序添加到全局命名空间的名字在 Brython 脚本中以模块browser中对象window的属性形式出现。基本用法script typetext/javascript circle {surface: function(r){return 3.14 * r * r}} /script script typetext/python from browser import document, window document[result].value window.circle.surface(10) /script浏览器模块的实际定义位于 www/src/Lib/browser/ 目录window在 Brython 内置模块中广泛使用例如 timer.py 中window.setInterval、window.setTimeout、window.requestAnimationFrame的直接调用以及 local_storage.py 中window.localStorage的访问都印证了这条通道的通用性。类型自动转换表JavaScript 对象在通过window暴露给 Brython 时会按如下规则转换为对应的 Python 对象JavaScript 对象js_objPython 对象window.js_objDOM 元素DOMNode实例DOM 事件DOMEvent实例DOM 元素集合DOMNode实例的列表true/falseTrue/Falsenull保持不变注 1undefined保持不变注 1数字Numberint或float实例大整数BigIntint实例字符串Stringstr实例数组Array保持不变注 2函数Function保持不变注 3补充说明null与undefined原样保留可用is与模块javascript中的常量NULL、UNDEFINED比较见 www/doc/en/javascript.mdJavaScript 数组保持不变但数组中的元素仍按本表逐项转换为 Python 对象若对象是函数调用该 JavaScript 函数时传入的 Python 参数会按本表的反向转换规则转换为 JavaScript 对象特别地Python 字典会转换为 JavaScript 对象且字典的键会被转换为字符串。其他 JS 对象包装为JSObject上述类型之外的其他 JavaScript 对象会被转换为模块javascript中定义的JSObject类的实例。JSObject是 Brython 内置类其实现位于 www/src/js_objects.js$B.JSObj $B.make_builtin_class(JSObject)第 657 行。它实现了 Python 侧的方法集[__getattr__, bind, bindings, unbind, to_dict]第 996 行支持属性访问、事件绑定与字典转换。JSObject实例可通过to_dict()方法转换为 Python 字典py_obj window.js_obj.to_dict()其实现细节见 js_objects.js遍历底层 JavaScript 对象的自有属性逐项用反向转换函数convert_to_python转换值后写入 Python 字典若对函数调用to_dict()会抛出TypeErrormethod to_dict() not supported for functions。模块javascript还提供了若干直接可用的名字便于在 Python 中操作 JavaScript 语言级对象详见 www/doc/en/javascript.mdDate日期/时间构造函数如Date.new(2012, 6, 10).toDateString()JSONstringify与parse两个函数用于序列化/反序列化简单对象字典、列表、元组、整数、实数、字符串Math数学函数与常量Number、String、RegExp对应 JavaScript 类型构造函数RegExp使用 JavaScript 特有语法与 Python 正则不完全一致其实例的exec()可作用于 Python 字符串NULL/UNDEFINED用于测试 JavaScript 对象是否为null/undefined。调用 JavaScript 函数时不能使用关键字参数JavaScript 函数无法用关键字参数调用——这会抛出TypeError。原因很直接Brython 脚本不知道 JavaScript 函数的签名。例如window.foo(y0, x1) # TypeError即使按期望顺序传参也无法被接受因为签名对 Python 侧不可见。对应地www/src/brython.js 中pyobj2jsobj包装 JS 函数时会显式检查arg.$kw并抛出TypeError(keyword arguments are not supported for Javascript functions)。使用 JavaScript 构造函数new特殊方法若 JavaScript 函数是构造函数在 JS 中用new调用Brython 为 JS 对象附加了一个特殊方法new用法与 JS 的new一致script typetext/javascript function Rectangle(x0, y0, x1, y1){ this.x0 x0 this.y0 y0 this.x1 x1 this.y1 y1 this.surface function(){return (x1 - x0) * (y1 - y0)} } /script script typetext/python from browser import alert, window rectangle window.Rectangle alert(rectangle.new(10, 10, 30, 30).surface()) /script从源码看js_objects.js 会识别以class开头的 JS 类并转换为 Python 类jsclass2pyclass普通 JS 函数则包装为可调用对象brython.js 中JSClass.tp_getattro对属性new返回一个函数其参数会先经pyobj2jsobj转换再以new语义执行——这正是文档所述new方法的实现基础。异常JavascriptError如果 Brython 调用的 JavaScript 脚本出错会触发类JavascriptError的异常实例Brython 代码可捕获它JavaScript 错误的堆栈会打印到sys.stderr。JavascriptError在 www/src/brython.js 中定义为内建异常类$B.make_builtin_class(JavascriptError, [_b_.Exception])其传播路径也体现在 brython.js 与 brython.js 等处。完整示例在 Brython 中使用 jQuery官方文档给出了一个较为完整的 jQuery 集成示例涵盖 Ajax 请求、DOM 操作、属性访问、事件回调和迭代html head script src//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js /script script src/src/brython.js/script /head script typetext/python from browser import window jq window.jQuery # Ajax call def onSuccess(data, status, req): print(data) print(status) jq.ajax(/cgi-bin/post_test.py, {data: {foo: 56}, success: onSuccess } ) # add an option to a SELECT box jq(#sel).append( optionthree) # access element attributes assert jq(#c).attr(id) c # define a callback for a click on a button def callback(ev): print(jq(ev.target).text()) jq(#btn).on(click, callback) # we can even use each to iterate on SPAN elements def show(i, obj): print(i, obj) jq.each(jq(span), show) /script body select idsel option valueoneone option valuetwotwo /select span idc/span button idbtnclick/button /body /html要点解读jq window.jQueryjQuery 暴露的全局对象jQuery$不是合法 Python 标识符因此必须使用jQuery名字经window访问Python 回调函数如onSuccess、callback、show作为参数传给 jQuery 时会按反向转换规则包装成 JavaScript 函数见 brython.js 的pyobj2jsobj对函数/方法的处理jq.ajax(...)的第二个参数是 Python 字典被转换为 JavaScript 对象键转为字符串因此success键携带的回调可被 jQuery 调用示例中请求的/cgi-bin/post_test.py在仓库中存在对应实现 www/cgi-bin/post_test.py。官方文档还提到gallery 中有更多使用 JavaScript 库Three、Highcharts、Raphael的完整示例例如基于 ES Module 的 three_webgl_interactive_cubes.html。把 JavaScript 库封装成 Python 模块除在页面script中加载库外另一种集成方式是创建一个可导入的 Python 模块脚本中只需import该模块无需在页面里单独加载库文件。前提是库文件可通过 Ajax 获取用模块browser的load(url)函数加载它库添加到全局 JS 命名空间的名字即可在 Python 模块中通过window暴露。例如创建模块jqueryfrom browser import window, load load(/path/to/jquery.min.js) # jQuery adds the name jQuery to the global Javascript namespace # (also called $, but this is not a valid Python identifier) jq window.jQuery页面中使用该模块时不再加载 jquery.jshtml head script srcbrython.js/script /head body script typetext/python import jquery jquery(#test).text(I can use jQuery here !) /script div idtest/div /body /html注意jq window.jQuery是模块级赋值因此jquery模块的命名空间中就有了可调用的jQuery对象jquery(#test)即等价于 jQuery 的$(#test)。这种封装思路把页面加载库的职责转移到了模块内部适合将第三方 JS 库沉淀为可复用 Python 包。从 JavaScript 程序使用 Brython 数据默认暴露的两个名字默认情况下Brython 只在全局 JavaScript 命名空间中暴露两个名字brython()触发页面中 Python 脚本执行的函数参见 Execution options__BRYTHON__Brython 内部使用的对象存储脚本执行所需的各种对象。因此默认情况下 JavaScript 程序无法直接访问 Brython 对象。例如要让 Brython 脚本中定义的echo()函数响应页面元素的事件不能直接写onclickecho()因为echo不在 JavaScript 命名空间中。正确做法是给元素设置 id然后用 Brython 侧绑定事件button idmybuttonfrom browser import document document[mybutton].bind(click, echo)另一种做法是强制把名字注入 JavaScript 命名空间将其定义为模块browser中window对象的属性from browser import window window.echo echo注意官方不推荐此方法因为它引入了与页面中 JavaScript 程序或库所定义名字发生冲突的风险。首选方案仍是上述bind()事件绑定。对象__BRYTHON__的属性__BRYTHON__暴露了一系列属性供 JavaScript 程序与同页 Python 脚本中定义的对象交互。其实现集中在 www/src/brython.js 顶部的初始化代码中第 285-311 行。__BRYTHON__.whenReady一个 JavaScriptPromise在 Brython 加载完成并可用时被 resolve。JavaScript 程序可据此保证在 Brython 就绪后再操作function use_brython(){ // code here } __BRYTHON__.whenReady.then(use_brython)实现上whenReady在 brython.js 中以new Promise(...)形式创建立即 resolve表示运行时已加载完毕正对应文档所述Brython 已加载、可被 JS 程序使用的语义。__BRYTHON__.getPythonModule(module_name)若名为module_name的 Python 模块已在页面中导入返回其对应的 JavaScript 对象该对象的属性即模块中定义的名字。对于页面内联的 Python 脚本module_name是script标签的id属性。script typetext/python ids1 from browser import alert def show_square(x): alert(x ** 2) /script Square of input idnum value2button idbtnshow/button script document.getElementById(btn).addEventListener(click, function(ev){ var v document.getElementById(num).value __BRYTHON__.getPythonModule(s1).show_square(parseInt(v)) } ) /script源码实现见 brython.jsgetPythonModule function(name){return $B.imported[name]}即从 Brython 内部按脚本 id 维护的模块注册表中取出模块对象。使用前提调用前必须确认该 Python 模块确实已加载并执行完毕。可以监听script typetext/python元素的load事件script typetext/python debug1 ids1 x 0 /script script var s1 document.getElementById(s1) s1.addEventListener(load, function(script){ var module __BRYTHON__.getPythonModule(s1) console.log(module.x) }) /script__BRYTHON__.pythonToJS(src[, script_id])将 Python 源码src转换为包含其 JavaScript 翻译结果的字符串该字符串可用eval()执行以触发脚本运行。实现位于 brython.js$B.pythonToJS $B.python_to_js其内部调用from_py(src, script_id)生成形如(function() {...return locals}())的 IIFE 字符串。__BRYTHON__.pythonToAST(src[, filename, mode])为 Python 源码src生成抽象语法树AST以 JavaScript 对象形式返回结构与 Python 官方文档 描述的 AST 一致。实现见 brython.js使用$B.Parser解析源码再经_PyPegen.run_parser产出 AST 对象。__BRYTHON__.runPythonSource(src[, attributes])执行 Python 源码src如同执行一个带指定attributes的脚本若attributes是字符串则视为脚本id否则必须是 JavaScript 对象。Execution options 中描述的选项如 debug 级别、import 路径等都可以作为属性传入。例如var src import some_module __BRYTHON__.runPythonSource(src, {pythonpath: my_modules, debug: 2})该方法返回表示该模块的 JavaScript 对象与__BRYTHON__.getPythonModule(script_id)等价。典型场景是运行页面中默认不执行的 Python 脚本——把script的type设为text/py-disabled由 JS 在合适时机触发script typetext/py-disabled ids1 from browser import alert string script s2 integer 8 real 3.14 dictionary {a: string, b: integer, c: real} alert(run py-disabled) /script button idbtnRun disabled/button script document.getElementById(btn).addEventListener(click, function(ev){ var script document.getElementById(s1), modobj __BRYTHON__.runPythonSource(script.innerText, {id: s1}) console.log(modobj) } ) /script实现上brython.js 会解析options字符串当作id对象则读取其id字段其余属性写入一个fakeScript的 options 中随后调用$B.run_script(script, src, script_id, url, true)完成编译执行最后返回$B.imported[script_id]。__BRYTHON__.importPythonModule(module_name[, attributes])按给定的attributes同上导入 Python 模块返回表示该模块的 JavaScript 对象。实现为 brython.jsreturn $B.runPythonSource(import name, options)——即把import name作为源码交给runPythonSource执行。__BRYTHON__.pyobj2jsobj(pyobj)将 Python 对象转换为对应的 JavaScript 对象遵循上文类型自动转换表的反向规则。这是整个双向桥接中Python → JS方向的底层转换函数定义于 brython.js它处理了布尔值、None/null/undefined、已包装对象的回取pyobj[JSOBJ]、list/tuple 的逐项映射、dict 的键字符串化、str 取原始值、big int/float 取数值、以及 Python 函数到 JS 回调包装含 async 函数的协程桥接等分支。双向转换的底层实现从源码结构可以归纳出整个互操作机制的核心组件方向核心函数位置JS → Pythonjsobj2pyobj含$B.JSObj.$factorywww/src/js_objects.jsPython → JS$B.pyobj2jsobjwww/src/brython.jsJS 对象包装类JSObjectto_dict等方法www/src/js_objects.jsJS 类转 Python 类jsclass2pyclass、JSClass.tp_getattronewwww/src/brython.js运行时桥接 APIwhenReady/getPythonModule/pythonToJS/pythonToAST/runPythonSource/importPythonModule/pyobj2jsobjwww/src/brython.js异常类型JavascriptErrorwww/src/brython.js此外模块javascript文档见 www/doc/en/javascript.md还提供import_js(url[, alias])与import_modules(refs, callback)两个导入工具前者按 URL 加载传统 JS 脚本并以$module对象作为命名空间后者按 ES Module 规范导入一个或多个模块全部加载完成后调用回调每个 URL 对应回调的一个参数。仓库 gallery 中的 three.js 示例即使用了这一机制。小结与最佳实践综合官方文档 jsobjects.md 与源码实现可以提炼出几条实用准则Brython → JS所有 JS 全局名都从from browser import window出发访问注意类型转换表、不能传关键字参数、构造函数用.new(...)、错误捕获JavascriptErrorJS → Brython默认只能依赖brython()与__BRYTHON__优先用bind()而非污染全局命名空间window.echo echo不推荐必须等whenReady或脚本load事件之后再操作 Python 模块库集成页面级集成直接用script加载后经window使用模块级集成用load() 模块封装或javascript.import_js/import_modules导入按需执行runPythonSourcetext/py-disabled是让 JS 决定何时运行 Python的标准模式配合{pythonpath: ..., debug: ...}等 执行选项 可灵活控制运行环境。以上全部示例均可直接在包含brython.js仓库内位于 www/src/brython.js另有压缩版 www/src/brython.min.js的 HTML 页面中运行验证。赞分享编程语言语言运行时编译器前端【免费下载链接】brythonBrython (Browser Python) is an implementation of Python 3 running in the browser项目地址https://gitcode.com/gh_mirrors/br/brython点击查看免费下载相关推荐NocoBase HTTP 遥测导出器配置指南用 TELEMETRY_METRIC_READERhttp 将指标推送到外部监控系统NocoBase HTTP 遥测导出器配置指南用 TELEMETRY_METRIC_READERhttp 将指标推送到外部监控系统 本篇围绕 NocoBas编程语言语言运行时编译器前端SwiftNIO 与 Swift Concurrency 完全互操作指南从 Future 桥接到 NIOAsyncChannel 与异步 BootstrapSwiftNIO 与 Swift Concurrency 完全互操作指南从 Future 桥接到 NIOAsyncChannel 与异步 Bootstrap后端网络OpenTelemetry Go 与 OpenTracing 桥接指南使用 bridge/opentracing 无缝迁移与双向互操作OpenTelemetry Go 与 OpenTracing 桥接指南使用 bridge/opentracing 无缝迁移与双向互操作 导读 在存量系统中可观测性上一篇探索 nnFormer一个高效的Transformer架构用于医疗图像分析下一篇Open3D移动端移植方案iOS和Android的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考