phpy:连接 PHP 与 Python 生态

phpy项目介绍了首个版本的发布和优化,包括在进程内结合ZendVM和CPythonVM的高效运行机制,跨平台兼容性,以及在PHP-FPM环境中的使用示例。性能测试显示phpy在操作Python数据结构上优于原生Python。文章还讨论了异常捕获、IDE提示、安装依赖、动态链接库问题和内置方法支持等内容。
摘要由CSDN通过智能技术生成

上周我们发布了 phpy 项目的第一个版本,收到了大量 PHP 开发者的关注,许多开发者向我们提交问题、意见和建议、IssuesPull Request

经过一周的优化和修改完善,phpy 又得到了一些突破性进展。本文将详细解答大家广泛关注的问题,以及介绍第二个版本的变化:

运行原理

在进程内同时创建了 ZendVMCPython VM,直接在进程堆栈空间内使用 C 函数 互相调用, 开销只有 zval <-> PyObject 结构体转换,因此性能是非常高的。

phpy 基于 PHP 官方的 ZendAPIPython 官方的 Py C API 实现,没有其他外部的 C 库依赖。因此是可以实现跨平台的,LinuxWindowsmacOS 均可使用。

在 PHP-FPM 下使用

第一个版本中我们不建议在 PHP-FPM 环境下使用。在后续的测试发现在 PHP-FPM 环境下 import Python 包,仅第一次消耗比较多的时间,第二次直接使用了 Python sys.modules 中缓存的包,因此 phpy 是完全可以用于 PHP-FPMApache 等短生命周期环境下的。

甚至我们可以使用 phpy 使得一些对象在 PHP-FPM 下也能常驻内存。

$app = PyCore::import('app.user');  
$storage = $app\->storage;  
if (!isset($storage\['data'\])) {  
    $storage\['data'\] = uniqid();  
    var\_dump("no cache");  
    $o = new stdClass();  
    $o\->hello = uniqid();  
    $storage\['obj'\] = $o;  
} else {  
    var\_dump("cached");  
    var\_dump(strval($storage\['data'\]));  
    var\_dump($storage\['obj'\]);  
}

上面的代码将一个 Python 字典持久化了,在 PHP-FPM 下可以实现内存复用。

phpy 底层设置了内存安全边界,若 Python 中持久化了 PHP 对象,在请求结束后依然会销毁,并且将值设置为 NULL,不必担心出现内存错误

性能测试

压测脚本中创建了一个 PyDict ,分别读写 PHP 代码和 Python 代码执行 1000万次

  • PHP 版本PHP 8.2.3 (cli) (built: Mar 17 2023 15:06:57) (NTS)

  • Python 版本Python 3.11.5

  • 操作系统:Ubuntu 20.04

  • GCC 版本gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)

请注意此测试需要构造一个 1000 万元素的 HashTable,需要至少 2G 以上内存空间才可以运行
测试代码请参考 压力测试

结果对比

(base) htf@swoole-12:~/workspace/python-php/docs/benchmark$ php dict.php   
dict set: 4.663758 seconds  
dict get: 3.980076 seconds  
(base) htf@swoole-12:~/workspace/python-php/docs/benchmark$ php array.php   
array set: 1.578963 seconds  
array get: 0.831129 seconds  
(base) htf@swoole-12:~/workspace/python-php/docs/benchmark$ python dict.py   
dict set: 5.321664 seconds  
dict get: 4.969081 seconds  
(base) htf@swoole-12:~/workspace/python-php/docs/benchmark$

Python 测试为基准:

脚本名称SetGet
dict.php114%125%
array.php337%599%
  • phpyPHP 代码写入 PyDict 的性能比原生 Python14%,读取性能高 25%

  • PHP 写入 PHP Array 的性能比 Python 写入 Dict237%,读取高出了近 500%

异常捕获

最新版本支持了 PythonPHP 异常的融合,可以在 PHP 代码中捕获 Python 运行过程中触发的异常。

try {  
    PyCore::import('not\_exists');  
} catch (PyError $e) {  
    PyCore::print($e\->error);  
    PyCore::print($e\->type);  
    PyCore::print($e\->value);  
    PyCore::print($e\->traceback);  
}
  • 底层会自动将 $e->value 的字符串值设置为异常消息,可使用 $e->getMessage() 获取

  • PyError 未设置 $e->code 错误码,请勿使用

IDE 自动提示

phpy 提供了一个自动生成工具,可以生成 IDE 自动提示文件。使用方法:

cd phpy/tools  
php gen-lib.php \[Python 包名称\]

例如 matplotlib.pyplot

  • 直接导入:PyCore::import('matplotlib.pyplot')

  • 生成提示文件:php tools/gen-lib.php matplotlib.pyplot

也可以配置 tools/gen-all-lib.php 批量生成多个包的提示文件。

安装依赖

composer require swoole/phpy

使用 IDE 提示

require dirname(\_\_DIR\_\_, 2) . '/vendor/autoload.php';  
$plt = python\\matplotlib\\pyplot::import();  
  
$x = new PyList(\[1, 2, 3, 4\]);  
$y = new PyList(\[30, 20, 50, 60\]);  
$plt\->plot($x, $y);  
$plt\->show();

编译参数

第一个版本中我们使用了硬编码的 Python 开发目录,新版本可以使用编译参数来指定,并且底层还会自动识别 Python 版本。

现在 phpy 最低支持 Python 3.8PHP 8.1,另外我们增加了一个 Dockerfile 可以参考此文件来构建 phpy 的环境。

–with-python-dir

指定 Python 的安装路径,例如 /usr/bin/python 应该设置为 --with-python-dir=/usr
若使用 conda 安装 Python,应设置为 /opt/anaconda3

–with-python-version

指定 Python 的版本,例如 3.103.113.12,默认将使用 $python-dir/bin/python -V 来获取版本。

动态链接库问题

导入库时发生动态链接库错误,原因可能是 LD 路径错误导致,可设置环境变量指定 Python C 模块 动态库路径。

export LD\_LIBRARY\_PATH=/opt/anaconda3/lib  
php plot.php

这种方式仅对当前的 bash 会话有效,不会影响全局,更加安全。不要直接修改 /etc/ld.so.conf.d/*.conf 增加 /opt/anaconda3/lib,这可能会导致 libc 库冲突,可能会影响操作系统其他程序的正常运行。

支持全部 Python 内置方法

在第一个版本中,我们使用了 C 代码实现了一部分内置函数,第二个版本中我们直接设置了 PyCore::__callStatic() 魔术方法,对于 PyCore 静态方法调用会自动调用 Pythonbuiltins 模块对应的方法。支持了全部 Python 内置方法。

可参考 Built-in Functions 了解更多内置方法的使用

甚至我们可以使用 evalexecPHP 中执行 Python 代码。

$pycode = <<<PYCODE  
square = {  
    f'{prefix}{i}': i\*\*2 for i in range(n)  
}  
PYCODE;  
  
$globals = new PyDict(\[  
    'n' => 10,  
    'prefix' => 'square\_',  
\]);  
  
PyCore::exec($pycode, $globals);  
$this\->assertEquals(64, $globals\['square'\]\['square\_8'\]);  
$this\->assertEquals(16, $globals\['square'\]\['square\_4'\]);

迭代器支持

现在可以使用迭代器来遍历 Python 对象,可以完美支持 Pythonyield 生成器语法。

$iter = PyCore::iter($uname);  
$this\->assertTrue($iter instanceof PyIter);  
  
$list = \[\];  
while ($next = PyCore::next($iter)) {  
    $list\[\] = PyCore::scalar($next);  
}

PHP 单元测试

phpy 项目拥有完整的单测覆盖来保证稳定性,安装成功后可使用 composer test 或者 phpunit脚本进行测试。

Python 单元测试

phpy使用了pytest工具编写了Python调用PHP API的用例。可使用 pytest -v tests/ 来测试模块可用性。

更多例子

Numpy 科学计算

$np = PyCore::import('numpy');  
$rs = $np\->floor($np\->random->random(\[3, 4\])->\_\_mul\_\_(10));  
PyCore::print($rs);

matplotlib.pyplot 数学绘图

$plt = PyCore::import('matplotlib.pyplot');  
  
$x = new PyList(\[1, 2, 3, 4\]);  
$y = new PyList(\[30, 20, 50, 60\]);  
$plt\->plot($x, $y);  
$plt\->show();

以上就是“phpy:连接 PHP 与 Python 生态”的全部内容,希望对你有所帮助。

关于Python技术储备

学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

在这里插入图片描述

二、Python必备开发工具

img

三、Python视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

四、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

五、Python练习题

检查学习结果。

img

六、面试资料

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

img

最后祝大家天天进步!!

上面这份完整版的Python全套学习资料已经上传至CSDN官方,朋友如果需要可以直接微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】。

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值