Closed
Description
I've been using Python 3 enum.IntEnum
lately instead of pybind11 enum_<>
enums (although they're less straightforward to bind), since the former are (a) faster and (b) more ergonomic/Pythonic.
I wonder if we could either improve enum_
a little (a lot?), and/or provide a shortcut to bind standard library enums on Python 3?
I'll try to prove my point using this mini-example:
#include <pybind11/pybind11.h>
#include <stdexcept>
enum class Side1 : char { Right = 1, Left = -1 };
enum class Side2 : char { Right = 1, Left = -1 };
namespace pybind11 { namespace detail {
template<> struct type_caster<::Side1> {
static handle cast(const ::Side1& src, return_value_policy, handle) {
// this is the only flaky-ish part here; a better way to share `test_enums.Side1`?
static object cls = module::import("test_enums").attr("Side1");
if (!cls) throw std::runtime_error("unable to import test_enums.Side1");
static object v_right = cls.attr("Right"), v_left = cls.attr("Left");
switch (src) {
case ::Side1::Right: return v_right.inc_ref();
case ::Side1::Left: return v_left.inc_ref();
default: return {};
}
}
static PYBIND11_DESCR name() { return _("Side1"); }
};
} }
PYBIND11_PLUGIN(test_enums) {
namespace py = pybind11;
using namespace pybind11::literals;
py::module m("test_enums");
m.attr("Side1") = py::module::import("enum").attr("IntEnum")
("Side1", py::dict("Right"_a=1, "Left"_a=-1));
py::enum_<Side2>(m, "Side2")
.value("Right", Side2::Right).value("Left", Side2::Left);
m.def("f1", []() { return Side1::Left; });
m.def("f2", []() { return Side2::Left; });
return m.ptr();
}
- Casting is 3x-4x faster:
>>> from test_enums import *
>>> %timeit f1()
10000000 loops, best of 3: 84.7 ns per loop
>>> %timeit f2()
1000000 loops, best of 3: 305 ns per loop
- Comparison is 4x-5x faster:
>>> e1, e2 = f1(), f2()
>>> %timeit e1 == e1
10000000 loops, best of 3: 40 ns per loop
>>> %timeit e2 == e2
1000000 loops, best of 3: 212 ns per loop
- singleton values versus new instances:
>>> f1() is f1()
True
>>> f2() is f2()
False
enum.IntEnum
type:
>>> list(Side1)
[<Side1.Left: -1>, <Side1.Right: 1>] # ease of enumeration over values
>>> dict(Side1.__members__)
{'Left': <Side1.Left: -1>, 'Right': <Side1.Right: 1>}
enum_
values:
>>> Side2.Left.Left.Left.Left.Left.Left.Left # stop showing up in my tab completion!
Side2.Left
>>> int(Side2.Left)
TypeError: __int__ returned non-int (type str) # because `char` caster, I guess...
>>> isinstance(Side2.Left, int)
False
enum.IntEnum
values:
>>> isinstance(Side1.left, int) # no need to reinvent the wheel, IntEnum is a subclass of int
True
>>> Side1.Left.name
'Left'
>>> Side1.Left.value
-1
Metadata
Metadata
Assignees
Labels
No labels