PyO3学习3

类型转换:

与使用 Python 原生类型相比,使用 Rust 库类型作为函数参数会产生转换成本。使用 Python 原生类型几乎是零成本(它们只需要类似于 Python 内置函数的类型检查isinstance())。

能转换的类型比较多,都是常见基础类型不一一列举:https://pyo3.rs/main/conversions/tables.html

PyO3 提供了一些方便的特征来在 Python 类型和 Rust 类型之间进行转换。

extract/ FromPyObject

将Python对象转换成rust最简单的方法就是使用extract. 其实extract这个trait就来源于FromPyObject这个trait实现的:

pub fn extract<'a, D>(&'a self) -> PyResult<D>
    where
        D: FromPyObject<'a>, {FromPyObject::extract(self)}

官方书上的例子确实跑不起来,看我的例子吧:

#[pyfunction]
pub fn test_trans(list:&PyList){
    let v: Vec<i32> = list.extract().unwrap();
    println!("Change is Ok {:?}", v);
}
// 这里没写在模块中注册,自己记得写一下
>>> from rust_give_python import *
>>> p = [1,2,3,4,5]
>>> test_trans(p)
Change is Ok [1, 2, 3, 4, 5]
>>> p = ['1','2','3','4','5']
>>> test_trans(p)
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: PyErr { type: <class 'TypeError'>, value: TypeError("'str' object cannot be interpreted as an integer"), traceback: None }', src\lib.rs:166:38

此外FromPyObject可以实现结构派生:派生代码将调用getattr方法获取响应属性,并最后使用extract方法转换:

#[derive(FromPyObject)]
pub struct tests_class_struct{
    pub string_test:String,
}

#[pyfunction]
pub fn trans_class(class_1:&PyAny){
    let q:tests_class_struct = class_1.extract().unwrap();
    println!("Translate_Ok");
}
// 同样的把注册的部分省略了
>>> class a:
...     def __init__(self):
...         self.string_test = "hello world"
>>> p = a()
>>> from rust_give_python import *
>>> trans_class(p)
Translate_Ok

我们也可以添加#[pyo3(item)]字段上设置属性,PyO3 将尝试通过调用get_itemPython 对象上的方法来提取值。这里就不作演示

关于derive(FromPyObject)的使用小贴士:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dinwn07U-1652888146101)(C:\Users\40629\Pictures\pyo3.png)]

IntoPy<T>

和上面Python转Rust的类型不同,这个类型就是为了Rust转Python用的,但实际上对于我们自定一的类型和方法用Pyclass其实绰绰有余的。换句话说我们基本的rust类型都实现了这个Trait, 而平时也不会直接调用而是调用它的高级分装版本,比如#[pyclass]

从Rust调用Python

任何 Python 原生对象引用(例如&PyAny&PyList&PyCell<MyClass>)都可用于调用 Python 函数。(当然,我感觉最常用的还是&PyAny) .

PyO3提供了两个接口用例调用Python的可调用对象:函数/对象的可调用方法:

  • call- 调用任何可调用的 Python 对象。接受args和kwards作为参数接口
  • call_method- 在 Python 对象上调用方法。接受args和kwards作为参数接口
  • call1 & call_method1 只接受args参数
  • call0&call_method2不接受参数调用函数。

例子:

fn main() -> PyResult<()> {
        let arg1 = "arg1";
        let arg2 = "arg2";
        let arg3 = "arg3";

        Python::with_gil(|py| {
            let fun: Py<PyAny> = PyModule::from_code(
                py,
                "def example(*args, **kwargs):
                if args != ():
                    return 1
                if kwargs != {}:
                    return 2
                if args == () and kwargs == {}:
                    return 0",
                "",
                "",
            )?.getattr("example")?.into();
            // call object without empty arguments
            let p = fun.call0(py).unwrap();
            println!("{}",p);
            // call object with PyTuple
            let args = PyTuple::new(py, &[arg1, arg2, arg3]);
            let p: PyObject = fun.call1(py, args)?;
            println!("{}",p);
            // pass arguments as rust tuple
            let args = ();
            let mut kwards = PyDict::new(py);
            kwards.set_item("1","2");
            let p = fun.call(py, args, Option::from(kwards))?;
            println!("{}",p);
            Ok(())
        })
}
0
1
2

当然,对于现有的引入包的文件也是可以跑的:

fn main() {
    // 这里我填写的绝对路径
    let py_foo = include_str!(".../import_try.py");
     Python::with_gil(|py| {
         // 这个函数涉及到导入requests直接跑了
        let fuc = PyModule::from_code(py,py_foo,"","").unwrap().getattr("requests_try").unwrap();
        let q = fuc.call0().unwrap();
        println!("{}", q);
    });
}
200
# import_try.py
import requests
def requests_try():
    m = requests.get("https://www.baidu.com")
    return m.status_code
if __name__ == '__main__':
    print(requests_try())

需要注意的是,我们在Python::这个域里面写的东西域返回的东西的类型都在GIL锁的生命周期内,出GIL之后就无法使用

let a = Python::with_gil(|py| {
	...
	let q = fuc.call0().unwrap();
    q })
    ^ returning this value requires that `'1` must outlive `'2`

这样的传参是不可以执行的。这个时候我们就可以使用extract来强制类型转换了

let h:i32 = q.extract().unwrap();
h  )}

当然。关于GIL生命周期域可变类型可以参考 官网。这里只需要知道我们的结果想要在Rust代码中使用需要上一节的类型转换即可

关于GIL光放文档给出了这样的说法:全局解释器锁 (GIL) 对这种情况有所帮助,它确保只有一个线程可以同时使用 Python 解释器及其 API,而非 Python 操作(系统调用和扩展代码)可以解锁 GIL 。具体的含义将在下一节做验证.
ps.这两天在调GTK环境最后放弃了,之后应该会走fltk路线,开发最有成就感的不就是GUI么

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Jython是一种用于Java平台的Python实现,而PyO3是一个Python扩展库,它使得可以使用Rust编写Python扩展,因此不能直接使用PyO3开发Jython扩展。不过,Jython本身提供了一些API来编写Java扩展,可以使用这些API来开发Jython扩展。 在Jython中,可以使用Java的反射机制来访问Java类和方法。以下是一个使用Jython和Java互操作的示例: ```python import java import java.lang.reflect.Method # 获取Java String类 string_class = java.lang.String # 创建Java String实例 java_string = string_class("Hello, World!") # 获取Java String的length方法 length_method = string_class.getMethod("length") # 调用length方法 result = length_method.invoke(java_string) # 打印结果 print(result) ``` 在上面的示例中,通过反射机制获取了Java String类和length方法,然后使用Jython调用了Java方法并获取了结果。 在开发Jython扩展时,可以使用类似的方式来调用Java类和方法。需要注意的是,Jython扩展需要编译成Java类,然后可以在Java代码中使用。可以使用Jython提供的命令行工具来编译Jython扩展: ``` jythonc mymodule.py ``` 这将会生成一个Java类文件mymodule.class,然后可以在Java代码中使用它。例如,以下代码演示了如何在Java中调用Jython扩展: ```java import org.python.util.PythonInterpreter; import org.python.core.PyObject; public class MyJavaClass { public static void main(String[] args) { // 创建Python解释器 PythonInterpreter interp = new PythonInterpreter(); // 导入Jython模块 interp.exec("import mymodule"); // 调用Jython函数 PyObject result = interp.eval("mymodule.myfunc('Hello, World!')"); // 打印结果 System.out.println(result); } } ``` 在上面的示例中,使用PythonInterpreter类创建了一个Python解释器,并导入了Jython模块。然后调用了Jython函数并获取了结果。需要注意的是,Jython扩展需要注意和Java互操作的问题,例如类型转换和异常处理等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值