Pyo3学习2

#[args(…)]

方法参数。对于某些余姚default value的值进行定义,如果重新赋值会覆盖掉default-value

这里有个问题就是赋值只接受字符串格式,对于原来非字符串格式直接加“ 但是原来字符串格式的变换略微不同,详见如下例子:

#[args(years="10")]
pub fn add_year(&mut self, years:i32) ->PyResult<i32>{
        let k = self.year + years;
        println!("now I'm {} years old", k);
        Ok(k)
}

import rust_give_python as ru
if __name__ == '__main__':
    a = ru.test_class()
    print(a.add_year())

now I'm 90 years old
#[args(some_string="\"None\"")]
pub fn test_fuc(&mut self , some_string:&str){
        println!("{}", some_string)
}

import rust_give_python as ru
if __name__ == '__main__':
    a = ru.test_class()
    print(a.test_fuc("Hello"))
    print(a.test_fuc())
Hello
None
魔术方法

在Rust定义类的时候免不了要考虑魔术方法, 在PyO3中倒是给我们做了很方便的封装:只要如果方法的名称是公认的魔法方法,PyO3 会自动将其放入类型对象中。(原来的另一种方法被废弃了,[自 PyO3 0.16 起已弃用] 在与属性结合的特殊特征中#[pyproto]。)

Rust中可定义的魔术方法: str, repr(获取对象内存地址), hash, richcmp, getattribute, getattr, setattr, delattr, bool,call. 以及iter和next(没想到竟然支持迭代器)

魔术方法确实有点多,看官网文档把, 同时有大佬整理的图片,https://www.cnblogs.com/traditional/p/13611233.html, 这个人感觉还是挺厉害的

// 一个简单斐波那契数列的生成器例子
#[pyclass]
pub struct iter_f{
    small_num:i32,
    big_num:i32,
    max_num:i32,
}

#[pymethods]
impl iter_f {
    #[new]
    fn new() -> iter_f{
        iter_f{small_num:0,big_num:1, max_num:10}
    }
    fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
        slf
    }
}


#[pyproto]
impl PyIterProtocol for iter_f{

    fn __next__(mut slf: PyRefMut<Self>) ->IterNextOutput<i32, &'static str>{
        if slf.max_num > 0 {
            let a = slf.small_num;
            slf.small_num = slf.big_num;
            slf.big_num += a;
            slf.max_num -= 1;
            return IterNextOutput::Yield(slf.small_num);
        }
        else{
            return IterNextOutput::Return("Ended");
        }
    }
}

import rust_give_python as ru
if __name__ == '__main__':
    k = ru.iter_f()
    for p in k:
        print(p)
1 2 3 5 8 13 21 34 55

必要的时候我们要为自己的类实现GC保证回收:实现这两个方法__traverse____clear____traverse__必须调用visit.call()对另一个 Python 对象的每个引用。 __clear__必须清除对其他 Python 对象的任何可变引用(从而打破引用循环)。不可变引用不必被清除,因为每个循环必须至少包含一个可变引用。(上面这一段比较恶心,我们直接看例子):

use pyo3::prelude::*;
use pyo3::PyTraverseError;
use pyo3::gc::PyVisit;

#[pyclass]
struct ClassWithGCSupport {
    obj: Option<PyObject>,
}

#[pymethods]
impl ClassWithGCSupport {
    fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError> {
        if let Some(obj) = &self.obj {
            visit.call(obj)?
        }
        Ok(())
    }

    fn __clear__(&mut self) {
        // Clear reference, this decrements ref counter.
        self.obj = None;
    }
}
_call_ 装饰器

其实讲道理_call__也是魔术方法应该放在上一节,不过那样就有点太巨无霸了,我不喜欢,所以在这里放了__call_ .使用call的类可以包装为修饰器,不过这个装饰器的包裹有点意思我研究一下:

use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};

/// A function decorator that keeps track how often it is called.
///
/// It otherwise doesn't do anything special.
#[pyclass(name = "Counter")]
pub struct PyCounter {
    // We use `#[pyo3(get)]` so that python can read the count but not mutate it.
    #[pyo3(get)]
    count: u64,

    // This is the actual function being wrapped.
    wraps: Py<PyAny>,
}

#[pymethods]
impl PyCounter {
    // Note that we don't validate whether `wraps` is actually callable.
    //
    // While we could use `PyAny::is_callable` for that, it has some flaws:
    //    1. It doesn't guarantee the object can actually be called successfully
    //    2. We still need to handle any exceptions that the function might raise
    #[new]
    fn __new__(wraps: Py<PyAny>) -> Self {
        PyCounter { count: 0, wraps }
    }

    #[args(args = "*", kwargs = "**")]
    fn __call__(
        &mut self,
        py: Python<'_>,
        args: &PyTuple,
        kwargs: Option<&PyDict>,
    ) -> PyResult<Py<PyAny>> {
        self.count += 1;
        let name = self.wraps.getattr(py, "__name__")?;

        println!("{} has been called {} time(s).", name, self.count);

        // After doing something, we finally forward the call to the wrapped function
        let ret = self.wraps.call(py, args, kwargs)?;

        // We could do something with the return value of
        // the function before returning it
        Ok(ret)
    }
}

#[pymodule]
pub fn decorator(_py: Python<'_>, module: &PyModule) -> PyResult<()> {
    module.add_class::<PyCounter>()?;
    Ok(())
}

等价于:

class Counter:
    def __init__(self, wraps):
        self.count = 0
        self.wraps = wraps

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.wraps.__name__} has been called {self.count} time(s)")
        self.wraps(*args, **kwargs)
 
# 或等价于
def Counter(wraps):
    count = 0
    def call(*args, **kwargs):
        nonlocal count
        count += 1
        print(f"{wraps.__name__} has been called {count} time(s)")
        return wraps(*args, **kwargs)
    return call

我尝试利用wrap包裹函数完成多线程运行,然后(GIL罪大恶极):

#[pyfunction]
pub fn test_thread(wraps: Py<PyAny>, py: Python<'_>) {
    let m = wraps.clone();
    unsafe {
    // 我专门套了一层Rust让这种线程编译能过,然后
    let a = thread::spawn(move || {
        m.call0(py).unwrap()
    });
    let b = thread::spawn(move || { wraps.call0(py).unwrap() });
    a.join();
    b.join();
}
}

错误原因如下:因为GIL的缘故这个Py<PyAny> 不能传递,不能多线程,真的麻中麻

error[E0277]: *mut pyo3::Python<'static> cannot be sent between threads safely
–> src\lib.rs:164:13
|
164 | let a = thread::spawn(move || {
| ____________^^^^^^^^^^^^^-
| | |
| | *mut pyo3::Python<'static> cannot be sent between threads safely
165 | | m.call0(py).unwrap()
166 | | });
| |_____- within this [closure@src\lib.rs:164:27: 166:6]

继承

我们在PyO3中定义的类不允许继承,哪怕使用type这样的函数也无法继承父类:

from rust_give_python import *
k = type('h',(test_class,),{})
TypeError: type 'builtins.test_class' is not an acceptable base type

我翻了翻资料,猜测的理由:没有__class__方法。正常在Python中定义的对象都有__class__方法,而我们得到的type一般是 <class ‘__main__.*’>。很遗憾,虽然Rust拓展了很多魔法方法,但是并没有__ class__方法,这也是让人头疼的点之一。此外,继承之后这个类去调用继承的方法的时候应该也会用到__dict__查看方法名,这个东西也没有被实现,,

后来在github上发现了解决方法,阿哲,我是sb:

#[pyclass(subclass)]
pub struct test_class{
......
}
if __name__ == 'main':
    k = type('h',(test_class,),{})
    print(test_class)
    print(k)
<class 'builtins.test_class'>
<class '__main__.h'>

#继承了__str__方法
test_class:{
year:80
id:1
name:hello
}
# 这也同时说明,有没有dict跟继承毫无关系
{}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值