Deprecate handle::operator== in favor of object_api::is
diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h
index b780455..7698ae2 100644
--- a/include/pybind11/eigen.h
+++ b/include/pybind11/eigen.h
@@ -551,7 +551,7 @@
         object matrix_type = sparse_module.attr(
             rowMajor ? "csr_matrix" : "csc_matrix");
 
-        if (obj.get_type() != matrix_type.ptr()) {
+        if (!obj.get_type().is(matrix_type)) {
             try {
                 obj = matrix_type(obj);
             } catch (const error_already_set &) {
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index 62ee786..79d0268 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -278,7 +278,7 @@
                 chain = (detail::function_record *) rec_capsule;
                 /* Never append a method to an overload chain of a parent class;
                    instead, hide the parent's overloads in this case */
-                if (chain->scope != rec->scope)
+                if (!chain->scope.is(rec->scope))
                     chain = nullptr;
             }
             // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing
@@ -1274,7 +1274,7 @@
         using Alias = typename Class::type_alias;
         handle cl_type = cl;
         cl.def("__init__", [cl_type](handle self_, Args... args) {
-                if (self_.get_type() == cl_type)
+                if (self_.get_type().is(cl_type))
                     new (self_.cast<Base *>()) Base(args...);
                 else
                     new (self_.cast<Alias *>()) Alias(args...);
@@ -1708,7 +1708,7 @@
         Py_file_input, d.ptr(), d.ptr());
     if (result == nullptr)
         throw error_already_set();
-    if ((handle) d["self"] == Py_None)
+    if (d["self"].is_none())
         return function();
     Py_DECREF(result);
 #endif
diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h
index f176311..8093ead 100644
--- a/include/pybind11/pytypes.h
+++ b/include/pybind11/pytypes.h
@@ -110,6 +110,8 @@
     PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)")
         object call(Args&&... args) const;
 
+    /// Equivalent to ``obj is other`` in Python.
+    bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); }
     /// Equivalent to ``obj is None`` in Python.
     bool is_none() const { return derived().ptr() == Py_None; }
     PYBIND11_DEPRECATED("Use py::str(obj) instead")
@@ -167,10 +169,12 @@
     /// Return ``true`` when the `handle` wraps a valid Python object
     explicit operator bool() const { return m_ptr != nullptr; }
     /** \rst
-        Check that the underlying pointers are the same.
+        Deprecated: Check that the underlying pointers are the same.
         Equivalent to ``obj1 is obj2`` in Python.
     \endrst */
+    PYBIND11_DEPRECATED("Use obj1.is(obj2) instead")
     bool operator==(const handle &h) const { return m_ptr == h.m_ptr; }
+    PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead")
     bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; }
     PYBIND11_DEPRECATED("Use handle::operator bool() instead")
     bool check() const { return m_ptr != nullptr; }
diff --git a/tests/test_eval.cpp b/tests/test_eval.cpp
index 05cc7f0..9a8ac21 100644
--- a/tests/test_eval.cpp
+++ b/tests/test_eval.cpp
@@ -37,7 +37,7 @@
         );
         auto x = local["x"].cast<int>();
 
-        return result == py::none() && x == 42;
+        return result.is_none() && x == 42;
     });
 
     m.def("test_eval", [global]() {
@@ -55,7 +55,7 @@
 
         auto result = py::eval<py::eval_single_statement>("x = call_test()", py::dict(), local);
         auto x = local["x"].cast<int>();
-        return result == py::none() && x == 42;
+        return result.is_none() && x == 42;
     });
 
     m.def("test_eval_file", [global](py::str filename) {
@@ -66,7 +66,7 @@
         local["call_test2"] = py::cpp_function([&](int value) { val_out = value; });
 
         auto result = py::eval_file(filename, global, local);
-        return val_out == 43 && result == py::none();
+        return val_out == 43 && result.is_none();
     });
 
     m.def("test_eval_failure", []() {