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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章