Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pybind11 cheatsheet documentation #1201

Open
ezyang opened this issue Dec 1, 2017 · 5 comments
Open

pybind11 cheatsheet documentation #1201

ezyang opened this issue Dec 1, 2017 · 5 comments

Comments

@ezyang
Copy link

ezyang commented Dec 1, 2017

I have found the pybind11 documentation to be not so great for quickly trying to figure out "how the heck do I get from type A to type B". So I wrote my own notes. I'm interesting in getting these back into the official docs, but I am not sure what is the most appropriate form.

Here goes.


The important types:

  • py::handle - equivalent to PyObject* (no automatic refcounting)
  • py::object - does automatic refcounting. Subclass of py::handle.

Conversions

PyObject* ↔ py::handle

This is a trivial conversion. These two types are basically totally equivalent.

PyObject *x;

py::handle h = x; // implicit conversion OK
x = h.ptr(); // no implicit conversion to raw pointer

py::handle (or subclass) → py::object (or subclass)

The syntax here works for both py::object, and any of its subclasses (e.g., py::str, etc)
http://pybind11.readthedocs.io/en/stable/reference.html#_CPPv26object

All of these are unchecked. Do an isinstance check beforehand if necessary. (Note that if you try to do anything with the py::object subclass, pybind11 will probably throw an exception at that point, so you probably aren't completely hosed. But casts to py::foo things are not checked at the point you do the cast!)

Common gotcha: you MUST pass a template argument to py::cast if you're casting from a PyObject*, it will silently do the wrong thing if you don't.

py::handle h;

// Copying
auto o = py::reinterpret_borrow<py::object>(h); // h can be PyObject*
  // or
auto o = x.cast<py::object>();
  // or
auto o = py::cast<py::object>(h); // h can be PyObject*
// equivalent to:
//    Py_INCREF(x);
//    THPObjectPtr o(x);

// Moving
auto o = py::reinterpret_steal<py::object>(h); // h can be PyObject*
// equivalent to:
//    THPObjectPtr o(x);

py::object (or superclass) → py::handle

Stealing is pretty useful if you're trying to invoke a C API function that steals its argument.

py::object o;

// Borrows from o
py::handle h2 = o;
// refcount NOT increased; h2 is only live as long as o is

// Steals from o
py::handle h = o.release();
// o no longer valid

NB: a “copy” from object into handle doesn't make sense, because handle doesn't know how to manage its own memory.

NB2: these conversions are implemented by the implicit copy-constructor on py::handle,static casting the input py::object as a const py::handle& (in case you're like me and tried to find where in the code this conversion was implemented).

py::object → C++

py::handle h; // or py::object
CppType x = h.cast<CppType>(); // may throw cast_error
  // or
CppType x = py::cast<CppType>(h);
// x is a new copy unrelated to h

C++ → py::object

CppType x;

py::object o = py::cast(x);
  // or
py::object o = py::cast(x, return_value_policy::automatic);
// o is a new copy, but with a different policy it could be different

How do I...

// b = isinstance(x, ty)
py::handle x;
py::handle ty;
bool b = py::isinstance(x, ty);

// b = isinstance(x, str)
py::handle x;
bool b = py::isinstance<py::str>(x);

// b = isinstance(x, tuple)
py::handle x;
bool b = py::isinstance<py::tuple>(x);

// s = str(x)
py::handle x;
py::str s(x); // use the constructor

// b = hasattr(x, "something")
py::handle x;
bool b = py::hasattr(x, "something");
@lemairecarl
Copy link

lemairecarl commented Feb 27, 2018

Thanks a lot! I really struggled with "C++ → py::object", which as mentioned here, is undocumented. It should at least be in the Reference section of the docs, if not in the type conversion section.

@ebetica
Copy link

ebetica commented Jun 4, 2018

It's actually documented here, albeit a bit briefly.

@eacousineau
Copy link
Contributor

eacousineau commented Nov 20, 2019

The more intricate bits of ref counting, casting, etc., came up on Gitter here. If I have the time, I may try to incorporate this and a few other things in a PR for the docs (adding it to the currently terse docs).

@EricCousineau-TRI
Copy link
Collaborator

Slowly circling back, but relates #2362 -- documenting pytypes a bit more, perhaps with examples

\cc @YannickJadoul @henryiii @rwgk

@YannickJadoul
Copy link
Collaborator

YannickJadoul commented Sep 6, 2020

Agreed that the docs on these concepts should be expanded! Lots of these things can probably be added to https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html? I would argue to try and fit them into the existing way the docs are structured and laid out, though:

  • C++ <-> Python can be in the "Casting back and forth" section (this is currently missing e.g. py::cast<CustomClass>(object)). The examples and explanation from this issue should also and we should also clarify the difference between py::cast<CustomClass> and py::cast<CustomClass&> and py::cast<CustomClass*>.
  • Conversion between different Python wrappers (handle, object, and more specific classes) could then be added to a "Converting between Python types", or so? It might be interesting to still split this up into handle vs. object on the one hand, and the more specific object subtypes on the other?
    What's missing from the docs and the cheat sheet above is also the difference between converting an object to a type and just reinterpreting one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants