pybind11使用教程笔记__6_ functions---3_rest

1.Python objects as arguments

pybind11 使用thin C++ wrapper classes将python类型包裹,这样的thin C++ wrapper classes可以作为函数的参数,这样在C++中可以 直接使用python原生数据类型,如下使用python dict:

void print_dict(py::dict dict) {
    /* Easily interact with Python types */
    for (auto item : dict)
        std::cout << "key=" << std::string(py::str(item.first)) << ", "
                  << "value=" << std::string(py::str(item.second)) << std::endl;
}

It can be exported:

m.def("print_dict", &print_dict);

used in Python as usual:

>>> print_dict({'foo': 123, 'bar': 'hello'})
key=foo, value=123
key=bar, value=hello

For more information on using Python objects in C++, see Python C++ interface.

2. Accepting *args and **kwargs

Python provides a useful mechanism to define functions that accept arbitrary numbers of arguments and keyword arguments:

def generic(*args, **kwargs):
    ...  # do something with args and kwargs

Such functions can also be created using pybind11:

void generic(py::args args, py::kwargs kwargs) {
    /// .. do something with args
    if (kwargs)
        /// .. do something with kwargs
}

/// Binding code
m.def("generic", &generic);

The class py::args derives from py::tuple and py::kwargs derives from py::dict.
可以只是用两者中的一个,也可以都使用,但是要保证这两个作为函数形参的时候必须在函数形参的最后。

Note

When combining *args or **kwargs with Keyword arguments you should not include py::arg tags for the py::args and py::kwargs arguments.

3. Default arguments revisited

值得注意: 默认参数在声明的时候就已经转换为python object。看一下如下的例子:

py::class_<MyClass>("MyClass")
    .def("myFunction", py::arg("arg") = SomeType(123));

在这个例子中,pybind11必须已经能够处理 SomeType类型的数据结构,这个通过以往的py::class_<SomeType>来实现。否则程序会抛出异常。

另一个值得注意的地方是函数签名中的默认参数的预览部分(generated using the object’s repr method),如果这部分呢不可用,函数签名恐怕不是很有用。

FUNCTIONS
...
|  myFunction(...)
|      Signature : (MyClass, arg : SomeType = <SomeType object at 0x101b7b080>) -> NoneType
...

生成可读预览的一种办法是定义 SomeType.repr, 另一种办法是通过py::arg_v来实现。

py::class_<MyClass>("MyClass")
    .def("myFunction", py::arg_v("arg", SomeType(123), "SomeType(123)"));

有时候需要将默认值设置为空指针null pointer value,这种情况下注意将其转换为对应的数据类型,如下例:

py::class_<MyClass>("MyClass")
    .def("myFunction", py::arg("arg") = (SomeType *) nullptr);

4. Non-converting arguments

Certain argument types may support conversion from one type to another. Some examples of conversions are:

Implicit conversions declared using py::implicitly_convertible<A,B>()
Calling a method accepting a double with an integer argument
Calling a std::complex argument with a non-complex python type (for example, with a float). (Requires the optional pybind11/complex.h header).
Calling a function taking an Eigen matrix reference with a numpy array of the wrong type or of an incompatible data layout. (Requires the optional pybind11/eigen.h header).
This behaviour is sometimes undesirable: the binding code may prefer to raise an error rather than convert the argument. This behaviour can be obtained through py::arg by calling the .noconvert() method of the py::arg object, such as:

m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));

Attempting the call the second function (the one without .noconvert()) with an integer will succeed, but attempting to call the .noconvert() version will fail with a TypeError:

>>> floats_preferred(4)
2.0
>>> floats_only(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: floats_only(): incompatible function arguments. The following argument types are supported:
    1. (f: float) -> float

Invoked with: 4

You may, of course, combine this with the _a shorthand notation (see Keyword arguments) and/or Default arguments. It is also permitted to omit the argument name by using the py::arg() constructor without an argument name, i.e. by specifying py::arg().noconvert().

Note

When specifying py::arg options it is necessary to provide the same number of options as the bound function has arguments. Thus if you want to enable no-convert behaviour for just one of several arguments, you will need to specify a py::arg() annotation for each argument with the no-convert argument modified to py::arg().noconvert().

5. Allow/Prohibiting None arguments

When a C++ type registered with py::class_ is passed as an argument to a function taking the instance as pointer or shared holder (e.g. shared_ptr or a custom, copyable holder as described in Custom smart pointers), pybind allows None to be passed from Python which results in calling the C++ function with nullptr (or an empty holder) for the argument.

To explicitly enable or disable this behaviour, using the .none method of the py::arg object:

py::class_<Dog>(m, "Dog").def(py::init<>());
py::class_<Cat>(m, "Cat").def(py::init<>());
m.def("bark", [](Dog *dog) -> std::string {
    if (dog) return "woof!"; /* Called with a Dog instance */
    else return "(no dog)"; /* Called with None, dog == nullptr */
}, py::arg("dog").none(true));
m.def("meow", [](Cat *cat) -> std::string {
    // Can't be called with None argument
    return "meow";
}, py::arg("cat").none(false));

With the above, the Python call bark(None) will return the string “(no dog)”, while attempting to call meow(None) will raise a TypeError:

>>> from animals import Dog, Cat, bark, meow
>>> bark(Dog())
'woof!'
>>> meow(Cat())
'meow'
>>> bark(None)
'(no dog)'
>>> meow(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: meow(): incompatible function arguments. The following argument types are supported:
    1. (cat: animals.Cat) -> str

Invoked with: None

The default behaviour when the tag is unspecified is to allow None.

Note

Even when .none(true) is specified for an argument, None will be converted to a nullptr only for custom and opaque types. Pointers to built-in types (double *, int *, …) and STL types (std::vector *, …; if pybind11/stl.h is included) are copied when converted to C++ (see Overview) and will not allow None as argument. To pass optional argument of these copied types consider using std::optional

6. Overload resolution order

When a function or method with multiple overloads is called from Python, pybind11 determines which overload to call in two passes. The first pass attempts to call each overload without allowing argument conversion (as if every argument had been specified as py::arg().noconvert() as described above).

If no overload succeeds in the no-conversion first pass, a second pass is attempted in which argument conversion is allowed (except where prohibited via an explicit py::arg().noconvert() attribute in the function definition).

If the second pass also fails a TypeError is raised.

Within each pass, overloads are tried in the order they were registered with pybind11.

What this means in practice is that pybind11 will prefer any overload that does not require conversion of arguments to an overload that does, but otherwise prefers earlier-defined overloads to later-defined ones.

Note

pybind11 does not further prioritize based on the number/pattern of overloaded arguments. That is, pybind11 does not prioritize a function requiring one conversion over one requiring three, but only prioritizes overloads requiring no conversion at all to overloads that require conversion of at least one argument.

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读