python get()函数_Boost::python实现C++和Python的混合编程(导出和调用)

7257b36b513febbf2db34076658cbbf8.png

boost::python用于将C++的函数和对象导出,方便python调用对象和方法,用来实现C++和Python的混合编程。

编译boost::python库和建立VS工程参考下文

Jason:Boost::python实现C++和Python的混合编程(配置篇)​zhuanlan.zhihu.com
bd5e83872541605536ad2f80ce068120.png

以下是各种基本类型导出的格式和关键字

Function/函数

boost::python::def()
char const* greet()
{
    return "Hello world!";
}

BOOST_PYTHON_MODULE(boost_python)
{
    boost::python::def("greet", greet);
}

//python
>>> import boost_python
>>> boost_python.greet()
'Hello world!'

Const/常量属性

boost::python::scope().attr
  • boost::python::scope()用于得到当前的作用域,需要注意的恰恰就是当前作用域
  • 定义新的scope对象会改变当前的作用域
class Message
{
public:
    void set(std::string msg)
    {
        this->msg = msg;
    }

    std::string get()
    {
        return this->msg;
    }

private:
    std::string msg;
};

//1.在模块中加入常量属性
BOOST_PYTHON_MODULE(boost_python)
{
    //const
    boost::python::scope().attr("yes") = 1;
    boost::python::scope().attr("no") = 0;

    boost::python::class_<Message>("Message")
        .def("set", &Message::set)
        .def("get", &Message::get); 
}

//python
>>> import boost_python
>>> boost_python.yes
1
>>> boost_python.no
0

///
//2.改变导出顺序,也没有问题,在模块中加入常量属性
BOOST_PYTHON_MODULE(boost_python)
{
    boost::python::class_<Message>("Message")
        .def("set", &Message::set)
        .def("get", &Message::get); 

    //const
    boost::python::scope().attr("yes") = 1;
    boost::python::scope().attr("no") = 0;
}

//python
>>> import boost_python
>>> boost_python.yes
1
>>> boost_python
0

//
//3.如果使用boost::python::scope对象,则改变了当前的作用域,yes和no成了message类的属性
BOOST_PYTHON_MODULE(boost_python)
{
    //Change the current scope
    boost::python::scope scopechange = boost::python::class_<Message>("Message")
        .def("set", &Message::set)
        .def("get", &Message::get); 

    //const Defined in the current scope(Message)
    boost::python::scope().attr("yes") = 1;
    boost::python::scope().attr("no") = 0;
}

//python
>>> import boost_python
>>> boost_python.yes
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'yes'
>>> boost_python.no
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'no'
>>> msg = boost_python.Message()
>>> msg.yes
1
>>> msg.no
0
>>>
Boost.Python - <boost/python/scope.hpp> - 1.49.0​www.boost.org
  • The scope class has an associated global Python object which controls the Python namespace in which new extension classes and wrapped functions will be defined as attributes. //作用域类具有关联的全局 python 对象, 该对象控制 python 命名空间, 在该命名空间中, 新的扩展类和包装的函数将被定义为属性。
  • Default-constructing a new scope object binds it to the associated global Python object. Constructing a scope object with an argument changes the associated global Python object to the one held by the argument, until the lifetime of the scope object ends, at which time the associated global Python object reverts to what it was before the scope object was constructed.//使用参数来构造一个新的scope对象会将关联的全局 python 对象更改为参数所持有的对象 直到作用域对象的生存期结束, 关联的全局 python 对象才会恢复到作用域对象之前的对象。
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/scope.hpp>
using namespace boost::python;

struct X
{
  void f() {}

  struct Y { int g() { return 42; } };
};

BOOST_PYTHON_MODULE(nested)
{
   // add some constants to the current (module) scope
   scope().attr("yes") = 1;
   scope().attr("no") = 0;

   // Change the current scope 
   scope outer
       = class_<X>("X")
            .def("f", &X::f)
            ;

   // Define a class Y in the current scope, X
   class_<X::Y>("Y")
      .def("g", &X::Y::g)
      ;
}

//python
>>> import nested
>>> nested.yes
1
>>> y = nested.X.Y()
>>> y.g()
421

Class of Default Constructor/默认构造函数的类

boost::python::class_<T>("TName")
class Message
{
public:
    void set(std::string msg)
    {
        this->msg = msg;
    }

    std::string get()
    {
        return this->msg;
    }

private:
    std::string msg;
};

BOOST_PYTHON_MODULE(boost_python)
{
    //class of default constructor
    boost::python::class_<Message>("Message")
        .def("set", &Message::set)
        .def("get", &Message::get); 
        //.def("print", &Message::get); //不可以使用print这种关键字作为导出函数名
}

//python>>> import boost_python
>>> msg=boost_python.Message()
>>> msg.get()
''
>>> msg.set("hello")
>>> msg.get()
'hello'

Class of Custom Constructor/自定义构造函数的类

boost::python::class_<T>("TName", boost::python::init<para>())
class Sum
{
public:
    Sum(std::string msg): m_msg(msg) {}
    Sum(double a, double b) : m_a(a), m_b(b) {}
    void set(std::string msg)
    {
        this->m_msg = msg;
    }
    std::string get()
    {
        return this->m_msg;
    }
    double result()
    {
        return m_a + m_b;
    }

private:
    std::string m_msg;
    double m_a;
    double m_b;
};

BOOST_PYTHON_MODULE(boost_python)
{
    //class of custom constructor
    boost::python::class_<Sum>("Sum", boost::python::init<std::string>()) //default constructor
        .def(boost::python::init<double, double>()) //custom constructor
        .def("set", &Sum::set)
        .def("get", &Sum::get)
        .def("result", &Sum::result);
}


//python
>>> import boost_python
>>> s = boost_python.Sum() //Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    Sum.__init__(Sum)
did not match C++ signature:
    __init__(struct _object * __ptr64, double, double)
    __init__(struct _object * __ptr64, class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)
>>> s = boost_python.Sum("hello")
>>> s.get()
'hello'

>>> sum = boost_python.Sum(3,4)
>>> sum.result()
7.0

Data member of Class/类的数据成员

.def_readonly()/.def_readwrite()
class Var
{
public:
    Var(std::string name) :m_name(name), m_value() {}
    std::string m_name;
    float m_value;
};

BOOST_PYTHON_MODULE(boost_python)
{
    //data member of class
    boost::python::class_<Var>("Var", boost::python::init<std::string>())
        .def_readonly("name", &Var::m_name)
        .def_readwrite("value", &Var::m_value);
}

//python
>>> import boost_python
>>> v = boost_python.Var("Micky")
>>> v.name
'Micky'

>>> v.name = "hello" //Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> v.value = "hello" //Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    None.None(Var, str)
did not match C++ signature:
    None(class Var {lvalue}, float)
>>> v.value = 123
>>> v.value
123.0

Add Properties to Class/给导出的类添加属性(类似于Delphi)

class NewMessage
{
public:
    void set(std::string msg)
    {
        this->msg = msg;
    }

    std::string get()
    {
        return this->msg;
    }

private:
    std::string msg;
};


BOOST_PYTHON_MODULE(boost_python)
{
    //add properties to class 
    boost::python::class_<NewMessage>("NewMessage", boost::python::init<>()) 
        .add_property("msg", &NewMessage::get, &NewMessage::set);
}


//python
>>> import boost_python
>>> newMsg = boost_python.NewMessage()
>>> newMsg.get()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NewMessage' object has no attribute 'get'
>>> newMsg.msg
''
>>> newMsg.msg = "hello"
>>> newMsg.msg
'hello'

Inheritance Classes/继承类的导出

boost::python::class_<T, boost::python::bases<TBase>>("TName")
  • 必须告知导出原C++类的继承关系,不然导出后的类之间就没有了继承关系
  • 告知类的继承关系关系后:
    • 继承类自动继承了基类的Python方法(即包装了的C++成员函数)
    • 即使是基类指针指向继承类对象,多态的函数也能够找到相应继承类的对应函数
class Base
{
public:
    virtual ~Base() {}
    virtual std::string getName()
    {
        return "Base";
    }
};

class Derived : public Base
{
public:
    std::string getName()
    {
        return "Derived";
    }
};

void BaseName(Base *base)
{
    std::cout << base->getName().c_str() << std::endl;
}

void DerivedName(Derived *derived)
{
    std::cout << derived->getName().c_str() << std::endl;
}

Base *factory()
{
    return new Derived;
}

BOOST_PYTHON_MODULE(boost_python)
{
    //inherited
    boost::python::class_<Base>("Base", boost::python::init<>())
        .def("getName", &Base::getName);
    boost::python::class_<Derived, boost::python::bases<Base>>("Derived", boost::python::init<>())
        .def("getName", &Derived::getName);

    boost::python::def("BaseName", BaseName);
    boost::python::def("DerivedName", DerivedName);
    //因为factory是生成一个新的Direved对象
    //manage_new_object告知Python生成一个指针指向一个新的Base的Python对象,直到这个对象被销毁
    boost::python::def("factory", factory, boost::python::return_value_policy<boost::python::manage_new_object>());
}

//python
>>> >>> import boost_python
>>> d = boost_python.factory()
>>> boost_python.BaseName(d)
Derived
>>> boost_python.DerivedName(d)
Derived
>>> d.getName()
'Derived'

>>> b = boost_python.Base()
>>> boost_python.BaseName(b)
Base
>>> boost_python.DerivedName(b) //Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    boost_python.DerivedName(Base)
did not match C++ signature:
    DerivedName(class Derived * __ptr64)
>>> b.getName()
'Base'

Pure Virtual Functions & Virtual Functions/纯虚函数和虚函数

boost::python::wrapper
boost::python::class_<TBase, boost::noncopyable>
boost::python::pure_virtual
  • 如果需要纯虚函数和虚函数的类能在导出后供Python继承,则新建一个包装类,它继承于基类并且同时继承boost::python::wrapper来对这个类进行封装,导出的时候,实际上导出的是新建的这个类
  • 纯虚函数
    • 包装类BasePVFWrap里对纯虚函数进行了规则处理,即调用的是继承类的虚函数
    • 导出是使用boost::python::pure_virtual告知哪个是纯虚函数。
class BasePVF
{
public:
    virtual ~BasePVF() {}
    virtual int f() = 0;
};

class BasePVFWrap : public BasePVF, public boost::python::wrapper<BasePVF>
{
public:
    int f()
    {
        return this->get_override("f")();
    }
};

BOOST_PYTHON_MODULE(boost_python)
{
    //pure virtual function
    boost::python::class_<BaseVFWrap, boost::noncopyable>("BaseVF")
        .def("f", &BaseVF::f, &BaseVFWrap::default_f);
}

//python
>>> import boost_python
>>> object_pvf = boost_python.BasePVF()
>>> object_pvf.f() //Error, Pure virtual function called
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: Pure virtual function called
>>> class DerivedPVF(boost_python.BasePVF):
...     def f(self):
...             return 50
...
>>> object_derived_pvf = DerivedPVF()
>>> object_derived_pvf.f()
50
  • 虚函数
    • 包装类BaseVFWrap里对虚函数进行了规则处理,即调用是检查继承类里是否有虚函数的重写,如果没有,调用基类的虚函数(因为基类的虚函数有实现)
    • 导出时,使得f属性关联两个函数,如果没有规则处理函数f,则调用基类的虚函数
class BaseVF
{
public:
    virtual ~BaseVF() {}
    virtual int f() { return 0; }
};

class BaseVFWrap : public BaseVF, public boost::python::wrapper<BaseVF>
{
public:
    int f()
    {
        if (boost::python::override f = this->get_override("f"))
        {
            return f();
        }
        return BaseVF::f();
    }

    int default_f() {
        return this->BaseVF::f();
    }
};

BOOST_PYTHON_MODULE(boost_python)
{
    //virtual function
    boost::python::class_<BasePVFWrap, boost::noncopyable>("BasePVF")
        .def("f", boost::python::pure_virtual(&BasePVF::f));
}


//python
>>> object_fv = boost_python.BaseVF()
>>> object_fv.f()
0
>>> class DerivedVF(boost_python.BaseVF):
...     def f(self):
...             return 100
...
>>> object_derived_vf = DerivedVF()
>>> object_derived_vf.f()
100

Operators and Special Functions of Class/类的运算符和特殊函数

class Operand
{
public:
    Operand() : m_num(0) {}
    Operand(int num) : m_num(num) {}
    int get()
    {
        return this->m_num;
    }
    Operand& operator+(const Operand& other);
    Operand& operator+(int num);
    Operand& operator-(const Operand& other);
    Operand& operator-(int num);
    Operand& operator+=(const Operand& other);
    Operand& operator+=(int num);
    Operand& operator-=(const Operand& other);
    Operand& operator-=(int num);
    bool operator<(const Operand& other);

    int abs();

private:
    int m_num;
};

Operand& Operand::operator+(const Operand& other)
{
    m_num += other.m_num;
    return *this;
}

Operand& Operand::operator+(int num)
{
    m_num += num;
    return *this;
}

Operand& Operand::operator-(const Operand& other)
{
    m_num -= other.m_num;
    return *this;
}

Operand& Operand::operator-(int num)
{
    m_num -= num;
    return *this;
}

Operand& Operand::operator+=(const Operand& other)
{
    return operator+(other);
}

Operand& Operand::operator+=(int num)
{
    return operator+(num);
}

Operand& Operand::operator-=(const Operand& other)
{
    return operator-(other);
}

Operand& Operand::operator-=(int num)
{
    return operator-(num);
}

bool Operand::operator<(const Operand& other)
{
    if (m_num < other.m_num)
    {
        return true;
    }
    return false;
}

int Operand::abs()
{
    m_num = std::abs(m_num);
    return m_num;
}

std::ostream& operator<<(std::ostream& out, Operand opr)
{
    out << opr.get();
    return out;
}


BOOST_PYTHON_MODULE(boost_python)
{
    boost::python::class_<Operand>("Operand", boost::python::init<>())
        .def(boost::python::init<int>())
        .def("get", &Operand::get)
        .def(boost::python::self + boost::python::self)
        .def(boost::python::self + int())
        .def(boost::python::self - boost::python::self)
        .def(boost::python::self - int())
        .def(boost::python::self += boost::python::self)
        .def(boost::python::self += int())
        .def(boost::python::self -= boost::python::self)
        .def(boost::python::self -= int())
        .def(boost::python::self < boost::python::self)
        .def("abs", &Operand::abs)
        .def(str(boost::python::self));
}


//python
>>> import boost_python
>>> opr1 = boost_python.Operand(10)
>>> opr2 = boost_python.Operand(20)
>>> opr1+opr2
<boost_python.Operand object at 0x000000000389A528>
>>> opr1.get()
30
>>> opr3 = boost_python.Operand(10)
>>> opr3 + 50
<boost_python.Operand object at 0x0000000002EFA528>
>>> opr3.get()
60

//python
>>> import boost_python
>>> opr1 = boost_python.Operand(10)
>>> opr1.get()
10
>>> opr1 += 50
>>> opr1.get()
60
>>> opr2 = boost_python.Operand(30)
>>> opr2.get()
30
>>> opr1 - opr2
<boost_python.Operand object at 0x000000000369A528>
>>> opr1.get()
30
>>> opr1 -= opr2
>>> opr1.get()
0
>>> opr1 - 10
<boost_python.Operand object at 0x000000000369A528>
>>> opr1.get()
-10
>>> opr5 = boost_python.Operand(30)
>>> opr6 = boost_python.Operand(40)
>>> opr5 < opr6
True
>>> opr5 > opr6
False
>>> opr5 - 40
<boost_python.Operand object at 0x0000000002F0A528>
>>> opr5.get()
-10
>>> opr5.abs()
10
>>> opr5.get()
10
>>> str(opr6)
'40'

Object's Lifecycle

  • 使用boost::python::return_internal_reference,表明返回的对象和传入的那个参数有关,但实际应用并无意义
  • 使用boost:::python:with_custodian_and_ward,表明参数之间的相互依赖关系,这个没有试过,欢迎指正
class A
{
public:
    int m_a;
};

class C
{
public:
    A m_a;
};

A& getA(C &pc)
{
    return pc.m_a;
}

BOOST_PYTHON_MODULE(boost_python)
{
    //lifecycle
    boost::python::class_<A>("A", boost::python::init<>())
.def_readwrite("value", &A::m_a);

    boost::python::class_<C>("C", boost::python::init<>())
.def_readwrite("a", &C::m_a);

    boost::python::def("getA", getA, boost::python::return_internal_reference<1>());
}


//python
>>> import boost_python
>>> cObj = boost_python.C()
>>> aObj = boost_python.getA(cObj)
>>> aObj
<boost_python.A object at 0x000000000388A528>
>>> cObj.a
<boost_python.A object at 0x0000000003A15528> //并不是一个对象
>>> del cObj
>>> aObj
<boost_python.A object at 0x000000000388A528> //依然存在

Function Overloading/函数重载

class Calc
{
public:
    int fun(int a)
    {
        return a;
    }
    int fun(int a, int b)
    {
        return a + b;
    }
    int fun(int a, int b, int c)
    {
        return a + b - c;
    }
};

//重载声明
int(Calc::*fun1)(int) = &Calc::fun;
int(Calc::*fun2)(int, int) = &Calc::fun;
int(Calc::*fun3)(int, int, int) = &Calc::fun;

BOOST_PYTHON_MODULE(boost_python)
{
    //function overloading of class
    boost::python::class_<Calc>("Calc", boost::python::init<>())
        .def("fun", fun1)
        .def("fun", fun2)
        .def("fun", fun3);
}

//python
>>> import boost_python
>>> c = boost_python.Calc()
>>> c.fun(3)
3
>>> c.fun(3,5)
8
>>> c.fun(3,5,10)
-2
>>> c.fun(3,5,10,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    Calc.fun(Calc, int, int, int, int)
did not match C++ signature:
    fun(class Calc {lvalue}, int, int, int)
    fun(class Calc {lvalue}, int, int)
    fun(class Calc {lvalue}, int)

Function's Default Parameters of Class/类函数的默认参数

boost::python::optional //类构造函数的可选参数标识
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS //定义导出函数名,最少参数和最多参数


class NewCalc
{
public:
    NewCalc()
    {
        m_value = 0;
    }
    NewCalc(int a, double b = 2.0, int c = 10)
    {
        m_value = a + b + c;
    }
    double fun(int a, double b = 3.0, int c = 5)
    {
        return a + b - c;
    }
    double Value()
    {
        return m_value;
    }
private:
    double m_value;
};


//1为最少参数,3为最多参数
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(NewCalc_overloads, fun, 1, 3);

BOOST_PYTHON_MODULE(boost_python)
{
    //function's default parameters  of class
    boost::python::class_<NewCalc>("NewCalc", boost::python::init<>())
        .def(boost::python::init<int, boost::python::optional<double, int>>())
        .def("fun", &NewCalc::fun, NewCalc_overloads())
        .def("Value", &NewCalc::Value);

}

//python
>>> import boost_python
>>> cNew = boost_python.NewCalc()
>>> cNew.Value()
0.0
>>> cNew1 = boost_python.NewCalc(10)
>>> cNew1.Value()
22.0
>>> cNew2 = boost_python.NewCalc(10, 30)
>>> cNew2.Value()
50.0
>>> cNew3 = boost_python.NewCalc(10, 30,50)
>>> cNew3.Value()
90.0
>>> cNew3 = boost_python.NewCalc(10, 30, 50, 20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    NewCalc.__init__(NewCalc, int, int, int, int)
did not match C++ signature:
    __init__(struct _object * __ptr64, int)
    __init__(struct _object * __ptr64, int, double)
    __init__(struct _object * __ptr64, int, double, int)
    __init__(struct _object * __


//python
>>> import boost_python
>>> cNew = boost_python.NewCalc()
>>> cNew.fun(10)
8.0
>>> cNew.fun(10, 20)
25.0
>>> cNew.fun(1, 4)
0.0
>>> cNew.fun(1, 4, 10)
-5.0
>>> cNew.fun(1, 4, 10, 5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    NewCalc.fun(NewCalc, int, int, int, int)
did not match C++ signature:
    fun(class NewCalc {lvalue}, int)
    fun(class NewCalc {lvalue}, int, double)
    fun(class NewCalc {lvalue}, int, double, int)

Function's Default Parameters/普通函数的默认参数

BOOST_PYTHON_FUNCTION_OVERLOADS //定义导出函数名,最少参数和最多参数
double FunDefaultPara(int a, unsigned int b = 2, double c = 3.0)
{
    return a + b + c;
}

BOOST_PYTHON_FUNCTION_OVERLOADS(FunDefaultPara_overload, FunDefaultPara, 1, 3);

BOOST_PYTHON_MODULE(boost_python)
{
    //function's default parameters 
    boost::python::def("FunDefaultPara", FunDefaultPara, FunDefaultPara_overload());
}

>>> import boost_python
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'boost_python']
>>> boost_python.FunDefaultPara(1)
6.0
>>> boost_python.FunDefaultPara(1,5)
9.0
>>> boost_python.FunDefaultPara(1,5,10)
16.0
>>> boost_python.FunDefaultPara(2,5,10)
17.0
>>> boost_python.FunDefaultPara(2,5,10)
17.0
>>> boost_python.FunDefaultPara(2)
7.0
>>> boost_python.FunDefaultPara(2,5,10,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    boost_python.FunDefaultPara(int, int, int, int)
did not match C++ signature:
    FunDefaultPara(int)
    FunDefaultPara(int, unsigned int)
    FunDefaultPara(int, unsigned int, double)

Smart Pointer/智能指针

boost::python::register_ptr_to_python<TP>()

class Data
{
public:
    void setData(int data)
    {
        m_data = data;
    }

    int getData()
    {
        return m_data;
    }

private:
    int m_data;
};

//smart pointer defined
typedef boost::shared_ptr<Data> DataPtr;

//smart pointer object
DataPtr pData(new Data);

BOOST_PYTHON_MODULE(boost_python)
{
    boost::python::class_<Data>("Data", boost::python::init<>())
        .add_property("data", &Data::getData, &Data::setData);

    boost::python::register_ptr_to_python<DataPtr>();

    boost::python::scope().attr("pData") = pData;
}

//python
>>> import boost_python
>>> boost_python.pData.data
-842150451
>>> boost_python.pData.data = 10
>>> boost_python.pData.data
10
  • 不加入boost::python::register_ptr_to_python描述导出智能指针,导入module时会报错

002492a8a9554ebbbc9eba64bcea389c.png
  • 只导出智能指针,不导出智能指针指向的类,也会报错

e050f738ed114b48b40e0ebb9b01fbd3.png
  • 本文所有代码均自己测试过
  • 同时也参考了以下博文,不胜感谢!
利用Boost.Python实现Python C/C++混合编程​blog.csdn.net
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值