Repository: RustPython/RustPython (language: rust)

Issue:
temporary fill up missing number protocols
cc @xiaozhiyan 


Discussion / hints:

Fix patch:
diff --git a/common/src/str.rs b/common/src/str.rs
index febab87f4e..7e2024d0f3 100644
--- a/common/src/str.rs
+++ b/common/src/str.rs
@@ -245,10 +245,10 @@ pub mod levenshtein {
         if a == b {
             return 0;
         }
-        if (b'A'..=b'Z').contains(&a) {
+        if a.is_ascii_uppercase() {
             a += b'a' - b'A';
         }
-        if (b'A'..=b'Z').contains(&b) {
+        if b.is_ascii_uppercase() {
             b += b'a' - b'A';
         }
         if a == b {
diff --git a/derive-impl/src/pyclass.rs b/derive-impl/src/pyclass.rs
index 07df8b31f6..45c366746c 100644
--- a/derive-impl/src/pyclass.rs
+++ b/derive-impl/src/pyclass.rs
@@ -548,14 +548,27 @@ where
                     other
                 ),
             };
-            quote_spanned! { ident.span() =>
-                class.set_str_attr(
-                    #py_name,
-                    ctx.make_funcdef(#py_name, Self::#ident)
-                        #doc
-                        #build_func,
-                    ctx,
-                );
+            if py_name.starts_with("__") && py_name.ends_with("__") {
+                let name_ident = Ident::new(&py_name, ident.span());
+                quote_spanned! { ident.span() =>
+                    class.set_attr(
+                        ctx.names.#name_ident,
+                        ctx.make_funcdef(#py_name, Self::#ident)
+                            #doc
+                            #build_func
+                        .into(),
+                    );
+                }
+            } else {
+                quote_spanned! { ident.span() =>
+                    class.set_str_attr(
+                        #py_name,
+                        ctx.make_funcdef(#py_name, Self::#ident)
+                            #doc
+                            #build_func,
+                        ctx,
+                    );
+                }
             }
         };
 
diff --git a/rust-toolchain b/rust-toolchain
deleted file mode 100644
index 2bf5ad0447..0000000000
--- a/rust-toolchain
+++ /dev/null
@@ -1,1 +0,0 @@
-stable
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000000..292fe499e3
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "stable"
diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs
index 69a317c828..8dbc180d83 100644
--- a/stdlib/src/array.rs
+++ b/stdlib/src/array.rs
@@ -6,6 +6,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyObjectRef {
     let array = module
         .get_attr("array", vm)
         .expect("Expect array has array type.");
+    array.init_builtin_number_slots(&vm.ctx);
 
     let collections_abc = vm
         .import("collections.abc", None, 0)
diff --git a/vm/src/builtins/bool.rs b/vm/src/builtins/bool.rs
index ed21290f65..417cc0e8d3 100644
--- a/vm/src/builtins/bool.rs
+++ b/vm/src/builtins/bool.rs
@@ -1,6 +1,11 @@
 use super::{PyInt, PyStrRef, PyType, PyTypeRef};
 use crate::{
-    class::PyClassImpl, convert::ToPyObject, function::OptionalArg, identifier, types::Constructor,
+    class::PyClassImpl,
+    convert::{ToPyObject, ToPyResult},
+    function::OptionalArg,
+    identifier,
+    protocol::PyNumberMethods,
+    types::{AsNumber, Constructor},
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject,
     VirtualMachine,
 };
@@ -102,7 +107,7 @@ impl Constructor for PyBool {
     }
 }
 
-#[pyclass(with(Constructor))]
+#[pyclass(with(Constructor, AsNumber))]
 impl PyBool {
     #[pymethod(magic)]
     fn repr(zelf: bool, vm: &VirtualMachine) -> PyStrRef {
@@ -166,6 +171,24 @@ impl PyBool {
     }
 }
 
+impl AsNumber for PyBool {
+    fn as_number() -> &'static PyNumberMethods {
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            and: Some(|number, other, vm| {
+                PyBool::and(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
+            }),
+            xor: Some(|number, other, vm| {
+                PyBool::xor(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
+            }),
+            or: Some(|number, other, vm| {
+                PyBool::or(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
+            }),
+            ..PyInt::AS_NUMBER
+        };
+        &AS_NUMBER
+    }
+}
+
 pub(crate) fn init(context: &Context) {
     PyBool::extend_class(context, context.types.bool_type);
 }
diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs
index 48639b818e..ae39e234cb 100644
--- a/vm/src/builtins/bytearray.rs
+++ b/vm/src/builtins/bytearray.rs
@@ -37,7 +37,6 @@ use crate::{
     VirtualMachine,
 };
 use bstr::ByteSlice;
-use once_cell::sync::Lazy;
 use std::mem::size_of;
 
 #[pyclass(module = false, name = "bytearray", unhashable = true)]
@@ -859,14 +858,16 @@ impl AsSequence for PyByteArray {
 
 impl AsNumber for PyByteArray {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            remainder: atomic_func!(|number, other, vm| {
-                PyByteArray::number_downcast(number)
-                    .mod_(other.to_owned(), vm)
-                    .to_pyresult(vm)
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            remainder: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PyByteArray>() {
+                    number.mod_(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs
index df18ac8457..a76ae966c9 100644
--- a/vm/src/builtins/bytes.rs
+++ b/vm/src/builtins/bytes.rs
@@ -629,14 +629,16 @@ impl AsSequence for PyBytes {
 
 impl AsNumber for PyBytes {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            remainder: atomic_func!(|number, other, vm| {
-                PyBytes::number_downcast(number)
-                    .mod_(other.to_owned(), vm)
-                    .to_pyresult(vm)
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            remainder: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PyBytes>() {
+                    number.mod_(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
diff --git a/vm/src/builtins/complex.rs b/vm/src/builtins/complex.rs
index c288c8e135..4146b29b60 100644
--- a/vm/src/builtins/complex.rs
+++ b/vm/src/builtins/complex.rs
@@ -1,6 +1,5 @@
 use super::{float, PyStr, PyType, PyTypeRef};
 use crate::{
-    atomic_func,
     class::PyClassImpl,
     convert::{ToPyObject, ToPyResult},
     function::{
@@ -15,7 +14,6 @@ use crate::{
 };
 use num_complex::Complex64;
 use num_traits::Zero;
-use once_cell::sync::Lazy;
 use rustpython_common::{float_ops, hash};
 use std::num::Wrapping;
 
@@ -454,38 +452,34 @@ impl Hashable for PyComplex {
 
 impl AsNumber for PyComplex {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            add: atomic_func!(|number, other, vm| {
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            add: Some(|number, other, vm| {
                 PyComplex::number_op(number, other, |a, b, _vm| a + b, vm)
             }),
-            subtract: atomic_func!(|number, other, vm| {
+            subtract: Some(|number, other, vm| {
                 PyComplex::number_op(number, other, |a, b, _vm| a - b, vm)
             }),
-            multiply: atomic_func!(|number, other, vm| {
+            multiply: Some(|number, other, vm| {
                 PyComplex::number_op(number, other, |a, b, _vm| a * b, vm)
             }),
-            power: atomic_func!(|number, other, vm| PyComplex::number_op(
-                number, other, inner_pow, vm
-            )),
-            negative: atomic_func!(|number, vm| {
+            power: Some(|number, other, vm| PyComplex::number_op(number, other, inner_pow, vm)),
+            negative: Some(|number, vm| {
                 let value = PyComplex::number_downcast(number).value;
                 (-value).to_pyresult(vm)
             }),
-            positive: atomic_func!(
-                |number, vm| PyComplex::number_downcast_exact(number, vm).to_pyresult(vm)
-            ),
-            absolute: atomic_func!(|number, vm| {
+            positive: Some(|number, vm| {
+                PyComplex::number_downcast_exact(number, vm).to_pyresult(vm)
+            }),
+            absolute: Some(|number, vm| {
                 let value = PyComplex::number_downcast(number).value;
                 value.norm().to_pyresult(vm)
             }),
-            boolean: atomic_func!(|number, _vm| Ok(PyComplex::number_downcast(number)
-                .value
-                .is_zero())),
-            true_divide: atomic_func!(|number, other, vm| {
+            boolean: Some(|number, _vm| Ok(PyComplex::number_downcast(number).value.is_zero())),
+            true_divide: Some(|number, other, vm| {
                 PyComplex::number_op(number, other, inner_div, vm)
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 
diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs
index 5590f856be..514dd54f58 100644
--- a/vm/src/builtins/dict.rs
+++ b/vm/src/builtins/dict.rs
@@ -479,20 +479,23 @@ impl AsSequence for PyDict {
 
 impl AsNumber for PyDict {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: atomic_func!(|num, args, vm| {
-                PyDict::number_downcast(num).or(args.to_pyobject(vm), vm)
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            or: Some(|num, args, vm| {
+                if let Some(num) = num.obj.downcast_ref::<PyDict>() {
+                    PyDict::or(num, args.to_pyobject(vm), vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            inplace_or: atomic_func!(|num, args, vm| {
-                PyDict::ior(
-                    PyDict::number_downcast(num).to_owned(),
-                    args.to_pyobject(vm),
-                    vm,
-                )
-                .map(|d| d.into())
+            inplace_or: Some(|num, args, vm| {
+                if let Some(num) = num.obj.downcast_ref::<PyDict>() {
+                    PyDict::ior(num.to_owned(), args.to_pyobject(vm), vm).map(|d| d.into())
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
@@ -1089,7 +1092,15 @@ trait ViewSetOps: DictView {
 }
 
 impl ViewSetOps for PyDictKeys {}
-#[pyclass(with(DictView, Constructor, Comparable, Iterable, ViewSetOps, AsSequence))]
+#[pyclass(with(
+    DictView,
+    Constructor,
+    Comparable,
+    Iterable,
+    ViewSetOps,
+    AsSequence,
+    AsNumber
+))]
 impl PyDictKeys {
     #[pymethod(magic)]
     fn contains(zelf: PyRef<Self>, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
@@ -1130,8 +1141,70 @@ impl AsSequence for PyDictKeys {
     }
 }
 
+impl AsNumber for PyDictKeys {
+    fn as_number() -> &'static PyNumberMethods {
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            subtract: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num
+                        .difference(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            and: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num
+                        .intersection(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            xor: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num.symmetric_difference(
+                        ArgIterable::try_from_object(vm, args.to_owned())?,
+                        vm,
+                    )?,
+                }
+                .into_pyobject(vm))
+            }),
+            or: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num.union(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            ..PyNumberMethods::NOT_IMPLEMENTED
+        };
+        &AS_NUMBER
+    }
+}
+
 impl ViewSetOps for PyDictItems {}
-#[pyclass(with(DictView, Constructor, Comparable, Iterable, ViewSetOps, AsSequence))]
+#[pyclass(with(
+    DictView,
+    Constructor,
+    Comparable,
+    Iterable,
+    ViewSetOps,
+    AsSequence,
+    AsNumber
+))]
 impl PyDictItems {
     #[pymethod(magic)]
     fn contains(zelf: PyRef<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
@@ -1186,6 +1259,60 @@ impl AsSequence for PyDictItems {
     }
 }
 
+impl AsNumber for PyDictItems {
+    fn as_number() -> &'static PyNumberMethods {
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            subtract: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num
+                        .difference(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            and: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num
+                        .intersection(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            xor: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num.symmetric_difference(
+                        ArgIterable::try_from_object(vm, args.to_owned())?,
+                        vm,
+                    )?,
+                }
+                .into_pyobject(vm))
+            }),
+            or: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num.union(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            ..PyNumberMethods::NOT_IMPLEMENTED
+        };
+        &AS_NUMBER
+    }
+}
+
 #[pyclass(with(DictView, Constructor, Iterable, AsSequence))]
 impl PyDictValues {
     #[pygetset]
diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs
index 38e1521393..d97cb40245 100644
--- a/vm/src/builtins/float.rs
+++ b/vm/src/builtins/float.rs
@@ -2,7 +2,6 @@ use super::{
     try_bigint_to_f64, PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef,
 };
 use crate::{
-    atomic_func,
     class::PyClassImpl,
     common::format::FormatSpec,
     common::{float_ops, hash},
@@ -21,7 +20,6 @@ use num_bigint::{BigInt, ToBigInt};
 use num_complex::Complex64;
 use num_rational::Ratio;
 use num_traits::{Signed, ToPrimitive, Zero};
-use once_cell::sync::Lazy;
 
 #[pyclass(module = false, name = "float")]
 #[derive(Debug, Copy, Clone, PartialEq)]
@@ -545,53 +543,32 @@ impl Hashable for PyFloat {
 
 impl AsNumber for PyFloat {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            add: atomic_func!(|num, other, vm| PyFloat::number_op(
-                num,
-                other,
-                |a, b, _vm| a + b,
-                vm
-            )),
-            subtract: atomic_func!(|num, other, vm| PyFloat::number_op(
-                num,
-                other,
-                |a, b, _vm| a - b,
-                vm
-            )),
-            multiply: atomic_func!(|num, other, vm| PyFloat::number_op(
-                num,
-                other,
-                |a, b, _vm| a * b,
-                vm
-            )),
-            remainder: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, inner_mod, vm)),
-            divmod: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, inner_divmod, vm)),
-            power: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, float_pow, vm)),
-            negative: atomic_func!(|num, vm| {
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            add: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a + b, vm)),
+            subtract: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a - b, vm)),
+            multiply: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a * b, vm)),
+            remainder: Some(|num, other, vm| PyFloat::number_op(num, other, inner_mod, vm)),
+            divmod: Some(|num, other, vm| PyFloat::number_op(num, other, inner_divmod, vm)),
+            power: Some(|num, other, vm| PyFloat::number_op(num, other, float_pow, vm)),
+            negative: Some(|num, vm| {
                 let value = PyFloat::number_downcast(num).value;
                 (-value).to_pyresult(vm)
             }),
-            positive: atomic_func!(
-                |num, vm| PyFloat::number_downcast_exact(num, vm).to_pyresult(vm)
-            ),
-            absolute: atomic_func!(|num, vm| {
+            positive: Some(|num, vm| PyFloat::number_downcast_exact(num, vm).to_pyresult(vm)),
+            absolute: Some(|num, vm| {
                 let value = PyFloat::number_downcast(num).value;
                 value.abs().to_pyresult(vm)
             }),
-            boolean: atomic_func!(|num, _vm| Ok(PyFloat::number_downcast(num).value.is_zero())),
-            int: atomic_func!(|num, vm| {
+            boolean: Some(|num, _vm| Ok(PyFloat::number_downcast(num).value.is_zero())),
+            int: Some(|num, vm| {
                 let value = PyFloat::number_downcast(num).value;
                 try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x))
             }),
-            float: atomic_func!(|num, vm| Ok(PyFloat::number_downcast_exact(num, vm))),
-            floor_divide: atomic_func!(|num, other, vm| {
-                PyFloat::number_op(num, other, inner_floordiv, vm)
-            }),
-            true_divide: atomic_func!(|num, other, vm| {
-                PyFloat::number_op(num, other, inner_div, vm)
-            }),
+            float: Some(|num, vm| Ok(PyFloat::number_downcast_exact(num, vm))),
+            floor_divide: Some(|num, other, vm| PyFloat::number_op(num, other, inner_floordiv, vm)),
+            true_divide: Some(|num, other, vm| PyFloat::number_op(num, other, inner_div, vm)),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 
diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs
index 7e9501f693..90f611974c 100644
--- a/vm/src/builtins/int.rs
+++ b/vm/src/builtins/int.rs
@@ -1,6 +1,5 @@
 use super::{float, PyByteArray, PyBytes, PyStr, PyType, PyTypeRef};
 use crate::{
-    atomic_func,
     builtins::PyStrRef,
     bytesinner::PyBytesInner,
     class::PyClassImpl,
@@ -20,7 +19,6 @@ use num_bigint::{BigInt, BigUint, Sign};
 use num_integer::Integer;
 use num_rational::Ratio;
 use num_traits::{One, Pow, PrimInt, Signed, ToPrimitive, Zero};
-use once_cell::sync::Lazy;
 use std::ops::{Div, Neg};
 use std::{fmt, ops::Not};
 
@@ -729,54 +727,7 @@ impl Hashable for PyInt {
 
 impl AsNumber for PyInt {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            add: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a + b, vm)),
-            subtract: atomic_func!(|num, other, vm| PyInt::number_op(
-                num,
-                other,
-                |a, b, _vm| a - b,
-                vm
-            )),
-            multiply: atomic_func!(|num, other, vm| PyInt::number_op(
-                num,
-                other,
-                |a, b, _vm| a * b,
-                vm
-            )),
-            remainder: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_mod, vm)),
-            divmod: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_divmod, vm)),
-            power: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_pow, vm)),
-            negative: atomic_func!(|num, vm| (&PyInt::number_downcast(num).value)
-                .neg()
-                .to_pyresult(vm)),
-            positive: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm).into())),
-            absolute: atomic_func!(|num, vm| PyInt::number_downcast(num)
-                .value
-                .abs()
-                .to_pyresult(vm)),
-            boolean: atomic_func!(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())),
-            invert: atomic_func!(|num, vm| (&PyInt::number_downcast(num).value)
-                .not()
-                .to_pyresult(vm)),
-            lshift: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_lshift, vm)),
-            rshift: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_rshift, vm)),
-            and: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a & b, vm)),
-            xor: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a ^ b, vm)),
-            or: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a | b, vm)),
-            int: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))),
-            float: atomic_func!(|num, vm| {
-                let zelf = PyInt::number_downcast(num);
-                try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x))
-            }),
-            floor_divide: atomic_func!(|num, other, vm| {
-                PyInt::number_op(num, other, inner_floordiv, vm)
-            }),
-            true_divide: atomic_func!(|num, other, vm| {
-                PyInt::number_op(num, other, inner_truediv, vm)
-            }),
-            index: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))),
-            ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        static AS_NUMBER: PyNumberMethods = PyInt::AS_NUMBER;
         &AS_NUMBER
     }
 
@@ -787,6 +738,34 @@ impl AsNumber for PyInt {
 }
 
 impl PyInt {
+    pub(super) const AS_NUMBER: PyNumberMethods = PyNumberMethods {
+        add: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a + b, vm)),
+        subtract: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a - b, vm)),
+        multiply: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a * b, vm)),
+        remainder: Some(|num, other, vm| PyInt::number_op(num, other, inner_mod, vm)),
+        divmod: Some(|num, other, vm| PyInt::number_op(num, other, inner_divmod, vm)),
+        power: Some(|num, other, vm| PyInt::number_op(num, other, inner_pow, vm)),
+        negative: Some(|num, vm| (&PyInt::number_downcast(num).value).neg().to_pyresult(vm)),
+        positive: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm).into())),
+        absolute: Some(|num, vm| PyInt::number_downcast(num).value.abs().to_pyresult(vm)),
+        boolean: Some(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())),
+        invert: Some(|num, vm| (&PyInt::number_downcast(num).value).not().to_pyresult(vm)),
+        lshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_lshift, vm)),
+        rshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_rshift, vm)),
+        and: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a & b, vm)),
+        xor: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a ^ b, vm)),
+        or: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a | b, vm)),
+        int: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))),
+        float: Some(|num, vm| {
+            let zelf = PyInt::number_downcast(num);
+            try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x))
+        }),
+        floor_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_floordiv, vm)),
+        true_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_truediv, vm)),
+        index: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))),
+        ..PyNumberMethods::NOT_IMPLEMENTED
+    };
+
     fn number_op<F, R>(number: PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult
     where
         F: FnOnce(&BigInt, &BigInt, &VirtualMachine) -> R,
diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index 023abdd246..25ddcb03be 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -1,5 +1,3 @@
-use once_cell::sync::Lazy;
-
 use super::{PyDict, PyDictRef, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef};
 use crate::{
     atomic_func,
@@ -10,6 +8,7 @@ use crate::{
     types::{AsMapping, AsNumber, AsSequence, Comparable, Constructor, Iterable, PyComparisonOp},
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
 };
+use once_cell::sync::Lazy;
 
 #[pyclass(module = false, name = "mappingproxy")]
 #[derive(Debug)]
@@ -70,7 +69,7 @@ impl Constructor for PyMappingProxy {
     }
 }
 
-#[pyclass(with(AsMapping, Iterable, Constructor, AsSequence, Comparable))]
+#[pyclass(with(AsMapping, Iterable, Constructor, AsSequence, Comparable, AsNumber))]
 impl PyMappingProxy {
     fn get_inner(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
         let opt = match &self.mapping {
@@ -232,15 +231,23 @@ impl AsSequence for PyMappingProxy {
 
 impl AsNumber for PyMappingProxy {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: atomic_func!(|num, args, vm| {
-                PyMappingProxy::number_downcast(num).or(args.to_pyobject(vm), vm)
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            or: Some(|num, args, vm| {
+                if let Some(num) = num.obj.downcast_ref::<PyMappingProxy>() {
+                    num.or(args.to_pyobject(vm), vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            inplace_or: atomic_func!(|num, args, vm| {
-                PyMappingProxy::number_downcast(num).ior(args.to_pyobject(vm), vm)
+            inplace_or: Some(|num, args, vm| {
+                if let Some(num) = num.obj.downcast_ref::<PyMappingProxy>() {
+                    num.ior(args.to_pyobject(vm), vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs
index 0814e17644..7a53093c79 100644
--- a/vm/src/builtins/set.rs
+++ b/vm/src/builtins/set.rs
@@ -805,61 +805,85 @@ impl Iterable for PySet {
 
 impl AsNumber for PySet {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            subtract: atomic_func!(|number, other, vm| {
-                PySet::number_downcast(number)
-                    .sub(other.to_owned(), vm)
-                    .to_pyresult(vm)
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            subtract: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    number.sub(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            and: atomic_func!(|number, other, vm| {
-                PySet::number_downcast(number)
-                    .and(other.to_owned(), vm)
-                    .to_pyresult(vm)
+            and: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    number.and(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            xor: atomic_func!(|number, other, vm| {
-                PySet::number_downcast(number)
-                    .xor(other.to_owned(), vm)
-                    .to_pyresult(vm)
+            xor: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    number.xor(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            or: atomic_func!(|number, other, vm| {
-                PySet::number_downcast(number)
-                    .or(other.to_owned(), vm)
-                    .to_pyresult(vm)
+            or: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    number.or(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            inplace_subtract: atomic_func!(|number, other, vm| {
-                PySet::isub(
-                    PySet::number_downcast(number).to_owned(),
-                    AnySet::try_from_object(vm, other.to_owned())?,
-                    vm,
-                )
-                .to_pyresult(vm)
+            inplace_subtract: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    PySet::isub(
+                        number.to_owned(),
+                        AnySet::try_from_object(vm, other.to_owned())?,
+                        vm,
+                    )
+                    .to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            inplace_and: atomic_func!(|number, other, vm| {
-                PySet::iand(
-                    PySet::number_downcast(number).to_owned(),
-                    AnySet::try_from_object(vm, other.to_owned())?,
-                    vm,
-                )
-                .to_pyresult(vm)
+            inplace_and: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    PySet::iand(
+                        number.to_owned(),
+                        AnySet::try_from_object(vm, other.to_owned())?,
+                        vm,
+                    )
+                    .to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            inplace_xor: atomic_func!(|number, other, vm| {
-                PySet::ixor(
-                    PySet::number_downcast(number).to_owned(),
-                    AnySet::try_from_object(vm, other.to_owned())?,
-                    vm,
-                )
-                .to_pyresult(vm)
+            inplace_xor: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    PySet::ixor(
+                        number.to_owned(),
+                        AnySet::try_from_object(vm, other.to_owned())?,
+                        vm,
+                    )
+                    .to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            inplace_or: atomic_func!(|number, other, vm| {
-                PySet::ior(
-                    PySet::number_downcast(number).to_owned(),
-                    AnySet::try_from_object(vm, other.to_owned())?,
-                    vm,
-                )
-                .to_pyresult(vm)
+            inplace_or: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    PySet::ior(
+                        number.to_owned(),
+                        AnySet::try_from_object(vm, other.to_owned())?,
+                        vm,
+                    )
+                    .to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
@@ -1105,29 +1129,37 @@ impl Iterable for PyFrozenSet {
 
 impl AsNumber for PyFrozenSet {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            subtract: atomic_func!(|number, other, vm| {
-                PyFrozenSet::number_downcast(number)
-                    .sub(other.to_owned(), vm)
-                    .to_pyresult(vm)
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            subtract: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PyFrozenSet>() {
+                    number.sub(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            and: atomic_func!(|number, other, vm| {
-                PyFrozenSet::number_downcast(number)
-                    .and(other.to_owned(), vm)
-                    .to_pyresult(vm)
+            and: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PyFrozenSet>() {
+                    number.and(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            xor: atomic_func!(|number, other, vm| {
-                PyFrozenSet::number_downcast(number)
-                    .xor(other.to_owned(), vm)
-                    .to_pyresult(vm)
+            xor: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PyFrozenSet>() {
+                    number.xor(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
-            or: atomic_func!(|number, other, vm| {
-                PyFrozenSet::number_downcast(number)
-                    .or(other.to_owned(), vm)
-                    .to_pyresult(vm)
+            or: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PyFrozenSet>() {
+                    number.or(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
diff --git a/vm/src/builtins/singletons.rs b/vm/src/builtins/singletons.rs
index 65081a0d46..28a142e103 100644
--- a/vm/src/builtins/singletons.rs
+++ b/vm/src/builtins/singletons.rs
@@ -1,8 +1,5 @@
-use once_cell::sync::Lazy;
-
 use super::{PyType, PyTypeRef};
 use crate::{
-    atomic_func,
     class::PyClassImpl,
     convert::ToPyObject,
     protocol::PyNumberMethods,
@@ -60,10 +57,10 @@ impl PyNone {
 
 impl AsNumber for PyNone {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            boolean: atomic_func!(|_number, _vm| Ok(false)),
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            boolean: Some(|_number, _vm| Ok(false)),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs
index 514db2bfc5..a34cd322c0 100644
--- a/vm/src/builtins/str.rs
+++ b/vm/src/builtins/str.rs
@@ -1301,14 +1301,16 @@ impl AsMapping for PyStr {
 
 impl AsNumber for PyStr {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            remainder: atomic_func!(|number, other, vm| {
-                PyStr::number_downcast(number)
-                    .modulo(other.to_owned(), vm)
-                    .to_pyresult(vm)
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            remainder: Some(|number, other, vm| {
+                if let Some(number) = number.obj.downcast_ref::<PyStr>() {
+                    number.modulo(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs
index 8aef931687..f26dcfef1b 100644
--- a/vm/src/builtins/type.rs
+++ b/vm/src/builtins/type.rs
@@ -3,7 +3,6 @@ use super::{
     PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak,
 };
 use crate::{
-    atomic_func,
     builtins::{
         descriptor::{
             DescrObject, MemberDef, MemberDescrObject, MemberGetter, MemberKind, MemberSetter,
@@ -28,7 +27,6 @@ use crate::{
 };
 use indexmap::{map::Entry, IndexMap};
 use itertools::Itertools;
-use once_cell::sync::Lazy;
 use std::{borrow::Borrow, collections::HashSet, fmt, ops::Deref, pin::Pin, ptr::NonNull};
 
 #[pyclass(module = false, name = "type")]
@@ -187,25 +185,6 @@ impl PyType {
 
         *slots.name.get_mut() = Some(String::from(name));
 
-        #[allow(clippy::mutable_key_type)]
-        let mut slot_name_set = HashSet::new();
-
-        for cls in mro.iter() {
-            for &name in cls.attributes.read().keys() {
-                if name != identifier!(ctx, __new__)
-                    && name.as_str().starts_with("__")
-                    && name.as_str().ends_with("__")
-                {
-                    slot_name_set.insert(name);
-                }
-            }
-        }
-        for &name in attrs.keys() {
-            if name.as_str().starts_with("__") && name.as_str().ends_with("__") {
-                slot_name_set.insert(name);
-            }
-        }
-
         let new_type = PyRef::new_ref(
             PyType {
                 base: Some(base),
@@ -220,9 +199,7 @@ impl PyType {
             None,
         );
 
-        for attr_name in slot_name_set {
-            new_type.update_slot::<true>(attr_name, ctx);
-        }
+        new_type.init_slots(ctx);
 
         let weakref_type = super::PyWeak::static_type();
         for base in &new_type.bases {
@@ -280,6 +257,30 @@ impl PyType {
         Ok(new_type)
     }
 
+    pub(crate) fn init_slots(&self, ctx: &Context) {
+        #[allow(clippy::mutable_key_type)]
+        let mut slot_name_set = std::collections::HashSet::new();
+
+        for cls in self.mro.iter() {
+            for &name in cls.attributes.read().keys() {
+                if name == identifier!(ctx, __new__) {
+                    continue;
+                }
+                if name.as_str().starts_with("__") && name.as_str().ends_with("__") {
+                    slot_name_set.insert(name);
+                }
+            }
+        }
+        for &name in self.attributes.read().keys() {
+            if name.as_str().starts_with("__") && name.as_str().ends_with("__") {
+                slot_name_set.insert(name);
+            }
+        }
+        for attr_name in slot_name_set {
+            self.update_slot::<true>(attr_name, ctx);
+        }
+    }
+
     pub fn slot_name(&self) -> String {
         self.slots.name.read().as_ref().unwrap().to_string()
     }
@@ -1061,12 +1062,12 @@ impl Callable for PyType {
 
 impl AsNumber for PyType {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: atomic_func!(|num, other, vm| {
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            or: Some(|num, other, vm| {
                 or_(num.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
@@ -1328,3 +1329,64 @@ mod tests {
         );
     }
 }
+
+impl crate::PyObject {
+    // temporary tool to fill missing number protocols for builtin types
+    pub fn init_builtin_number_slots(&self, ctx: &Context) {
+        let typ = self
+            .downcast_ref::<PyType>()
+            .expect("not called from a type");
+        macro_rules! call_update_slot {
+            ($name:ident) => {
+                let id = identifier!(ctx, $name);
+                if typ.has_attr(id) {
+                    typ.update_slot::<true>(identifier!(ctx, $name), ctx);
+                }
+            };
+        }
+        call_update_slot!(__add__);
+        call_update_slot!(__radd__);
+        call_update_slot!(__iadd__);
+        call_update_slot!(__sub__);
+        call_update_slot!(__rsub__);
+        call_update_slot!(__isub__);
+        call_update_slot!(__mul__);
+        call_update_slot!(__rmul__);
+        call_update_slot!(__imul__);
+        call_update_slot!(__mod__);
+        call_update_slot!(__rmod__);
+        call_update_slot!(__imod__);
+        call_update_slot!(__div__);
+        call_update_slot!(__rdiv__);
+        call_update_slot!(__idiv__);
+        call_update_slot!(__divmod__);
+        call_update_slot!(__rdivmod__);
+        call_update_slot!(__pow__);
+        call_update_slot!(__rpow__);
+        call_update_slot!(__ipow__);
+        call_update_slot!(__lshift__);
+        call_update_slot!(__rlshift__);
+        call_update_slot!(__ilshift__);
+        call_update_slot!(__rshift__);
+        call_update_slot!(__rrshift__);
+        call_update_slot!(__irshift__);
+        call_update_slot!(__and__);
+        call_update_slot!(__rand__);
+        call_update_slot!(__iand__);
+        call_update_slot!(__xor__);
+        call_update_slot!(__rxor__);
+        call_update_slot!(__ixor__);
+        call_update_slot!(__or__);
+        call_update_slot!(__ror__);
+        call_update_slot!(__ior__);
+        call_update_slot!(__floordiv__);
+        call_update_slot!(__rfloordiv__);
+        call_update_slot!(__ifloordiv__);
+        call_update_slot!(__truediv__);
+        call_update_slot!(__rtruediv__);
+        call_update_slot!(__itruediv__);
+        call_update_slot!(__matmul__);
+        call_update_slot!(__rmatmul__);
+        call_update_slot!(__imatmul__);
+    }
+}
diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs
index ad1899a04c..a5c11d3d2e 100644
--- a/vm/src/builtins/union.rs
+++ b/vm/src/builtins/union.rs
@@ -1,5 +1,3 @@
-use once_cell::sync::Lazy;
-
 use super::{genericalias, type_};
 use crate::{
     atomic_func,
@@ -13,6 +11,7 @@ use crate::{
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
     VirtualMachine,
 };
+use once_cell::sync::Lazy;
 use std::fmt;
 
 const CLS_ATTRS: &[&str] = &["__module__"];
@@ -257,12 +256,12 @@ impl AsMapping for PyUnion {
 
 impl AsNumber for PyUnion {
     fn as_number() -> &'static PyNumberMethods {
-        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: atomic_func!(|num, other, vm| {
+        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
+            or: Some(|num, other, vm| {
                 PyUnion::or(num.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
-        });
+        };
         &AS_NUMBER
     }
 }
diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs
index 4170c20df6..a41d843e9b 100644
--- a/vm/src/protocol/mod.rs
+++ b/vm/src/protocol/mod.rs
@@ -10,5 +10,7 @@ pub use buffer::{BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, V
 pub use callable::PyCallable;
 pub use iter::{PyIter, PyIterIter, PyIterReturn};
 pub use mapping::{PyMapping, PyMappingMethods};
-pub use number::{PyNumber, PyNumberMethods};
+pub use number::{
+    PyNumber, PyNumberBinaryFunc, PyNumberBinaryOp, PyNumberMethods, PyNumberUnaryFunc,
+};
 pub use sequence::{PySequence, PySequenceMethods};
diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs
index bbf88de6ab..f9a0ffb02a 100644
--- a/vm/src/protocol/number.rs
+++ b/vm/src/protocol/number.rs
@@ -7,11 +7,9 @@ use crate::{
     AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject,
     VirtualMachine,
 };
-use crossbeam_utils::atomic::AtomicCell;
 
-type UnaryFunc<R = PyObjectRef> = AtomicCell<Option<fn(PyNumber, &VirtualMachine) -> PyResult<R>>>;
-type BinaryFunc<R = PyObjectRef> =
-    AtomicCell<Option<fn(PyNumber, &PyObject, &VirtualMachine) -> PyResult<R>>>;
+pub type PyNumberUnaryFunc<R = PyObjectRef> = fn(PyNumber, &VirtualMachine) -> PyResult<R>;
+pub type PyNumberBinaryFunc = fn(PyNumber, &PyObject, &VirtualMachine) -> PyResult;
 
 impl PyObject {
     #[inline]
@@ -113,46 +111,45 @@ pub struct PyNumberMethods {
     /* Number implementations must check *both*
     arguments for proper type and implement the necessary conversions
     in the slot functions themselves. */
-    pub add: BinaryFunc,
-    pub subtract: BinaryFunc,
-    pub multiply: BinaryFunc,
-    pub remainder: BinaryFunc,
-    pub divmod: BinaryFunc,
-    pub power: BinaryFunc,
-    pub negative: UnaryFunc,
-    pub positive: UnaryFunc,
-    pub absolute: UnaryFunc,
-    pub boolean: UnaryFunc<bool>,
-    pub invert: UnaryFunc,
-    pub lshift: BinaryFunc,
-    pub rshift: BinaryFunc,
-    pub and: BinaryFunc,
-    pub xor: BinaryFunc,
-    pub or: BinaryFunc,
-    pub int: UnaryFunc<PyRef<PyInt>>,
-    pub float: UnaryFunc<PyRef<PyFloat>>,
+    pub add: Option<PyNumberBinaryFunc>,
+    pub subtract: Option<PyNumberBinaryFunc>,
+    pub multiply: Option<PyNumberBinaryFunc>,
+    pub remainder: Option<PyNumberBinaryFunc>,
+    pub divmod: Option<PyNumberBinaryFunc>,
+    pub power: Option<PyNumberBinaryFunc>,
+    pub negative: Option<PyNumberUnaryFunc>,
+    pub positive: Option<PyNumberUnaryFunc>,
+    pub absolute: Option<PyNumberUnaryFunc>,
+    pub boolean: Option<PyNumberUnaryFunc<bool>>,
+    pub invert: Option<PyNumberUnaryFunc>,
+    pub lshift: Option<PyNumberBinaryFunc>,
+    pub rshift: Option<PyNumberBinaryFunc>,
+    pub and: Option<PyNumberBinaryFunc>,
+    pub xor: Option<PyNumberBinaryFunc>,
+    pub or: Option<PyNumberBinaryFunc>,
+    pub int: Option<PyNumberUnaryFunc<PyRef<PyInt>>>,
+    pub float: Option<PyNumberUnaryFunc<PyRef<PyFloat>>>,
 
-    pub inplace_add: BinaryFunc,
-    pub inplace_subtract: BinaryFunc,
-    pub inplace_multiply: BinaryFunc,
-    pub inplace_remainder: BinaryFunc,
-    pub inplace_divmod: BinaryFunc,
-    pub inplace_power: BinaryFunc,
-    pub inplace_lshift: BinaryFunc,
-    pub inplace_rshift: BinaryFunc,
-    pub inplace_and: BinaryFunc,
-    pub inplace_xor: BinaryFunc,
-    pub inplace_or: BinaryFunc,
+    pub inplace_add: Option<PyNumberBinaryFunc>,
+    pub inplace_subtract: Option<PyNumberBinaryFunc>,
+    pub inplace_multiply: Option<PyNumberBinaryFunc>,
+    pub inplace_remainder: Option<PyNumberBinaryFunc>,
+    pub inplace_power: Option<PyNumberBinaryFunc>,
+    pub inplace_lshift: Option<PyNumberBinaryFunc>,
+    pub inplace_rshift: Option<PyNumberBinaryFunc>,
+    pub inplace_and: Option<PyNumberBinaryFunc>,
+    pub inplace_xor: Option<PyNumberBinaryFunc>,
+    pub inplace_or: Option<PyNumberBinaryFunc>,
 
-    pub floor_divide: BinaryFunc,
-    pub true_divide: BinaryFunc,
-    pub inplace_floor_divide: BinaryFunc,
-    pub inplace_true_divide: BinaryFunc,
+    pub floor_divide: Option<PyNumberBinaryFunc>,
+    pub true_divide: Option<PyNumberBinaryFunc>,
+    pub inplace_floor_divide: Option<PyNumberBinaryFunc>,
+    pub inplace_true_divide: Option<PyNumberBinaryFunc>,
 
-    pub index: UnaryFunc<PyRef<PyInt>>,
+    pub index: Option<PyNumberUnaryFunc<PyRef<PyInt>>>,
 
-    pub matrix_multiply: BinaryFunc,
-    pub inplace_matrix_multiply: BinaryFunc,
+    pub matrix_multiply: Option<PyNumberBinaryFunc>,
+    pub inplace_matrix_multiply: Option<PyNumberBinaryFunc>,
 }
 
 impl PyNumberMethods {
@@ -160,48 +157,112 @@ impl PyNumberMethods {
     // TODO: weak order read for performance
     #[allow(clippy::declare_interior_mutable_const)]
     pub const NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods {
-        add: AtomicCell::new(None),
-        subtract: AtomicCell::new(None),
-        multiply: AtomicCell::new(None),
-        remainder: AtomicCell::new(None),
-        divmod: AtomicCell::new(None),
-        power: AtomicCell::new(None),
-        negative: AtomicCell::new(None),
-        positive: AtomicCell::new(None),
-        absolute: AtomicCell::new(None),
-        boolean: AtomicCell::new(None),
-        invert: AtomicCell::new(None),
-        lshift: AtomicCell::new(None),
-        rshift: AtomicCell::new(None),
-        and: AtomicCell::new(None),
-        xor: AtomicCell::new(None),
-        or: AtomicCell::new(None),
-        int: AtomicCell::new(None),
-        float: AtomicCell::new(None),
-        inplace_add: AtomicCell::new(None),
-        inplace_subtract: AtomicCell::new(None),
-        inplace_multiply: AtomicCell::new(None),
-        inplace_remainder: AtomicCell::new(None),
-        inplace_divmod: AtomicCell::new(None),
-        inplace_power: AtomicCell::new(None),
-        inplace_lshift: AtomicCell::new(None),
-        inplace_rshift: AtomicCell::new(None),
-        inplace_and: AtomicCell::new(None),
-        inplace_xor: AtomicCell::new(None),
-        inplace_or: AtomicCell::new(None),
-        floor_divide: AtomicCell::new(None),
-        true_divide: AtomicCell::new(None),
-        inplace_floor_divide: AtomicCell::new(None),
-        inplace_true_divide: AtomicCell::new(None),
-        index: AtomicCell::new(None),
-        matrix_multiply: AtomicCell::new(None),
-        inplace_matrix_multiply: AtomicCell::new(None),
+        add: None,
+        subtract: None,
+        multiply: None,
+        remainder: None,
+        divmod: None,
+        power: None,
+        negative: None,
+        positive: None,
+        absolute: None,
+        boolean: None,
+        invert: None,
+        lshift: None,
+        rshift: None,
+        and: None,
+        xor: None,
+        or: None,
+        int: None,
+        float: None,
+        inplace_add: None,
+        inplace_subtract: None,
+        inplace_multiply: None,
+        inplace_remainder: None,
+        inplace_power: None,
+        inplace_lshift: None,
+        inplace_rshift: None,
+        inplace_and: None,
+        inplace_xor: None,
+        inplace_or: None,
+        floor_divide: None,
+        true_divide: None,
+        inplace_floor_divide: None,
+        inplace_true_divide: None,
+        index: None,
+        matrix_multiply: None,
+        inplace_matrix_multiply: None,
     };
+
+    pub fn binary_op(&self, op_slot: PyNumberBinaryOp) -> Option<PyNumberBinaryFunc> {
+        use PyNumberBinaryOp::*;
+        match op_slot {
+            Add => self.add,
+            Subtract => self.subtract,
+            Multiply => self.multiply,
+            Remainder => self.remainder,
+            Divmod => self.divmod,
+            Power => self.power,
+            Lshift => self.lshift,
+            Rshift => self.rshift,
+            And => self.and,
+            Xor => self.xor,
+            Or => self.or,
+            InplaceAdd => self.inplace_add,
+            InplaceSubtract => self.inplace_subtract,
+            InplaceMultiply => self.inplace_multiply,
+            InplaceRemainder => self.inplace_remainder,
+            InplacePower => self.inplace_power,
+            InplaceLshift => self.inplace_lshift,
+            InplaceRshift => self.inplace_rshift,
+            InplaceAnd => self.inplace_and,
+            InplaceXor => self.inplace_xor,
+            InplaceOr => self.inplace_or,
+            FloorDivide => self.floor_divide,
+            TrueDivide => self.true_divide,
+            InplaceFloorDivide => self.inplace_floor_divide,
+            InplaceTrueDivide => self.inplace_true_divide,
+            MatrixMultiply => self.matrix_multiply,
+            InplaceMatrixMultiply => self.inplace_matrix_multiply,
+        }
+    }
 }
 
+#[derive(Copy, Clone)]
+pub enum PyNumberBinaryOp {
+    Add,
+    Subtract,
+    Multiply,
+    Remainder,
+    Divmod,
+    Power,
+    Lshift,
+    Rshift,
+    And,
+    Xor,
+    Or,
+    InplaceAdd,
+    InplaceSubtract,
+    InplaceMultiply,
+    InplaceRemainder,
+    InplacePower,
+    InplaceLshift,
+    InplaceRshift,
+    InplaceAnd,
+    InplaceXor,
+    InplaceOr,
+    FloorDivide,
+    TrueDivide,
+    InplaceFloorDivide,
+    InplaceTrueDivide,
+    MatrixMultiply,
+    InplaceMatrixMultiply,
+}
+
+#[derive(Copy, Clone)]
 pub struct PyNumber<'a> {
     pub obj: &'a PyObject,
-    methods: &'a PyNumberMethods,
+    pub(crate) methods: &'a PyNumberMethods,
 }
 
 impl<'a> From<&'a PyObject> for PyNumber<'a> {
@@ -220,16 +281,9 @@ impl PyNumber<'_> {
         obj.class().mro_find_map(|x| x.slots.as_number.load())
     }
 
-    pub fn methods(&self) -> &PyNumberMethods {
-        self.methods
-    }
-
     // PyNumber_Check
     pub fn check(obj: &PyObject) -> bool {
-        let Some(methods) = Self::find_methods(obj) else {
-            return false;
-        };
-        let methods = methods.as_ref();
+        let methods = &obj.class().slots.number;
         methods.int.load().is_some()
             || methods.index.load().is_some()
             || methods.float.load().is_some()
@@ -238,12 +292,12 @@ impl PyNumber<'_> {
 
     // PyIndex_Check
     pub fn is_index(&self) -> bool {
-        self.methods().index.load().is_some()
+        self.obj.class().slots.number.index.load().is_some()
     }
 
     #[inline]
     pub fn int(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
-        self.methods().int.load().map(|f| {
+        self.obj.class().slots.number.int.load().map(|f| {
             let ret = f(self, vm)?;
             let value = if !ret.class().is(PyInt::class(vm)) {
                 warnings::warn(
@@ -267,7 +321,7 @@ impl PyNumber<'_> {
 
     #[inline]
     pub fn index(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
-        self.methods().index.load().map(|f| {
+        self.obj.class().slots.number.index.load().map(|f| {
             let ret = f(self, vm)?;
             let value = if !ret.class().is(PyInt::class(vm)) {
                 warnings::warn(
@@ -291,7 +345,7 @@ impl PyNumber<'_> {
 
     #[inline]
     pub fn float(self, vm: &VirtualMachine) -> Option<PyResult<PyRef<PyFloat>>> {
-        self.methods().float.load().map(|f| {
+        self.obj.class().slots.number.float.load().map(|f| {
             let ret = f(self, vm)?;
             let value = if !ret.class().is(PyFloat::class(vm)) {
                 warnings::warn(
diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs
index 529ef2d1cb..36c66ad8bb 100644
--- a/vm/src/protocol/sequence.rs
+++ b/vm/src/protocol/sequence.rs
@@ -2,7 +2,7 @@ use crate::{
     builtins::{type_::PointerSlot, PyList, PyListRef, PySlice, PyTuple, PyTupleRef},
     convert::ToPyObject,
     function::PyArithmeticValue,
-    protocol::PyMapping,
+    protocol::{PyMapping, PyNumberBinaryOp},
     AsObject, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
 };
 use crossbeam_utils::atomic::AtomicCell;
@@ -118,7 +118,7 @@ impl PySequence<'_> {
 
         // if both arguments apear to be sequences, try fallback to __add__
         if self.check() && other.to_sequence(vm).check() {
-            let ret = vm._add(self.obj, other)?;
+            let ret = vm.binary_op1(self.obj, other, PyNumberBinaryOp::Add)?;
             if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
                 return Ok(ret);
             }
@@ -137,7 +137,7 @@ impl PySequence<'_> {
 
         // fallback to __mul__
         if self.check() {
-            let ret = vm._mul(self.obj, &n.to_pyobject(vm))?;
+            let ret = vm.binary_op1(self.obj, &n.to_pyobject(vm), PyNumberBinaryOp::Multiply)?;
             if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
                 return Ok(ret);
             }
diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs
index fa2d917682..95929debcf 100644
--- a/vm/src/stdlib/builtins.rs
+++ b/vm/src/stdlib/builtins.rs
@@ -21,7 +21,7 @@ mod builtins {
             ArgBytesLike, ArgCallable, ArgIntoBool, ArgIterable, ArgMapping, ArgStrOrBytesLike,
             Either, FuncArgs, KwArgs, OptionalArg, OptionalOption, PosArgs, PyArithmeticValue,
         },
-        protocol::{PyIter, PyIterReturn},
+        protocol::{PyIter, PyIterReturn, PyNumberBinaryOp},
         py_io,
         readline::{Readline, ReadlineResult},
         stdlib::sys,
@@ -605,13 +605,7 @@ mod builtins {
             modulus,
         } = args;
         match modulus {
-            None => vm.call_or_reflection(
-                &x,
-                &y,
-                identifier!(vm, __pow__),
-                identifier!(vm, __rpow__),
-                |vm, x, y| Err(vm.new_unsupported_binop_error(x, y, "pow")),
-            ),
+            None => vm.binary_op(&x, &y, PyNumberBinaryOp::Power, "pow"),
             Some(z) => {
                 let try_pow_value = |obj: &PyObject,
                                      args: (PyObjectRef, PyObjectRef, PyObjectRef)|
@@ -954,6 +948,11 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
     crate::protocol::VecBuffer::make_class(&vm.ctx);
 
     builtins::extend_module(vm, &module);
+    use crate::AsObject;
+    ctx.types
+        .generic_alias_type
+        .as_object()
+        .init_builtin_number_slots(&vm.ctx);
 
     let debug_mode: bool = vm.state.settings.optimize == 0;
     extend_module!(vm, module, {
diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs
index 087d01008b..86dfc1e19a 100644
--- a/vm/src/stdlib/io.rs
+++ b/vm/src/stdlib/io.rs
@@ -1879,8 +1879,9 @@ mod _io {
         write_through: bool,
     }
 
-    #[derive(Debug, Copy, Clone)]
+    #[derive(Debug, Copy, Clone, Default)]
     enum Newlines {
+        #[default]
         Universal,
         Passthrough,
         Lf,
@@ -1888,13 +1889,6 @@ mod _io {
         Crlf,
     }
 
-    impl Default for Newlines {
-        #[inline]
-        fn default() -> Self {
-            Newlines::Universal
-        }
-    }
-
     impl Newlines {
         /// returns position where the new line starts if found, otherwise position at which to
         /// continue the search after more is read into the buffer
@@ -2054,8 +2048,9 @@ mod _io {
         data: PendingWritesData,
     }
 
-    #[derive(Debug)]
+    #[derive(Debug, Default)]
     enum PendingWritesData {
+        #[default]
         None,
         One(PendingWrite),
         Many(Vec<PendingWrite>),
@@ -2076,12 +2071,6 @@ mod _io {
         }
     }
 
-    impl Default for PendingWritesData {
-        fn default() -> Self {
-            PendingWritesData::None
-        }
-    }
-
     impl PendingWrites {
         fn push(&mut self, write: PendingWrite) {
             self.num_bytes += write.as_bytes().len();
diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index f0647d31e0..32975c91a4 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -1,14 +1,13 @@
-use crate::common::{hash::PyHash, lock::PyRwLock};
-use crate::convert::ToPyObject;
 use crate::{
     builtins::{type_::PointerSlot, PyFloat, PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef},
     bytecode::ComparisonOperator,
-    convert::ToPyResult,
+    common::{hash::PyHash, lock::PyRwLock},
+    convert::{ToPyObject, ToPyResult},
     function::{Either, FromArgs, FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
     identifier,
     protocol::{
-        PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberMethods, PySequence,
-        PySequenceMethods,
+        PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberBinaryFunc,
+        PyNumberBinaryOp, PyNumberMethods, PyNumberUnaryFunc, PySequence, PySequenceMethods,
     },
     vm::Context,
     AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
@@ -85,6 +84,7 @@ pub struct PyTypeSlots {
 
     // The count of tp_members.
     pub member_count: usize,
+    pub number: PyNumberSlots,
 }
 
 impl PyTypeSlots {
@@ -102,6 +102,128 @@ impl std::fmt::Debug for PyTypeSlots {
     }
 }
 
+#[derive(Default)]
+pub struct PyNumberSlots {
+    pub add: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub divmod: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub power: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub negative: AtomicCell<Option<PyNumberUnaryFunc>>,
+    pub positive: AtomicCell<Option<PyNumberUnaryFunc>>,
+    pub absolute: AtomicCell<Option<PyNumberUnaryFunc>>,
+    pub boolean: AtomicCell<Option<PyNumberUnaryFunc<bool>>>,
+    pub invert: AtomicCell<Option<PyNumberUnaryFunc>>,
+    pub lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub and: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub xor: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub or: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub int: AtomicCell<Option<PyNumberUnaryFunc<PyRef<PyInt>>>>,
+    pub float: AtomicCell<Option<PyNumberUnaryFunc<PyRef<PyFloat>>>>,
+
+    pub right_add: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_divmod: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_power: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_and: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_xor: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_or: AtomicCell<Option<PyNumberBinaryFunc>>,
+
+    pub inplace_add: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_power: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_and: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_xor: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_or: AtomicCell<Option<PyNumberBinaryFunc>>,
+
+    pub floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+
+    pub index: AtomicCell<Option<PyNumberUnaryFunc<PyRef<PyInt>>>>,
+
+    pub matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+}
+
+impl PyNumberSlots {
+    pub fn left_binary_op(
+        &self,
+        op_slot: PyNumberBinaryOp,
+    ) -> PyResult<Option<PyNumberBinaryFunc>> {
+        use PyNumberBinaryOp::*;
+        let binary_op = match op_slot {
+            Add => self.add.load(),
+            Subtract => self.subtract.load(),
+            Multiply => self.multiply.load(),
+            Remainder => self.remainder.load(),
+            Divmod => self.divmod.load(),
+            Power => self.power.load(),
+            Lshift => self.lshift.load(),
+            Rshift => self.rshift.load(),
+            And => self.and.load(),
+            Xor => self.xor.load(),
+            Or => self.or.load(),
+            InplaceAdd => self.inplace_add.load(),
+            InplaceSubtract => self.inplace_subtract.load(),
+            InplaceMultiply => self.inplace_multiply.load(),
+            InplaceRemainder => self.inplace_remainder.load(),
+            InplacePower => self.inplace_power.load(),
+            InplaceLshift => self.inplace_lshift.load(),
+            InplaceRshift => self.inplace_rshift.load(),
+            InplaceAnd => self.inplace_and.load(),
+            InplaceXor => self.inplace_xor.load(),
+            InplaceOr => self.inplace_or.load(),
+            FloorDivide => self.floor_divide.load(),
+            TrueDivide => self.true_divide.load(),
+            InplaceFloorDivide => self.inplace_floor_divide.load(),
+            InplaceTrueDivide => self.inplace_true_divide.load(),
+            MatrixMultiply => self.matrix_multiply.load(),
+            InplaceMatrixMultiply => self.inplace_matrix_multiply.load(),
+        };
+        Ok(binary_op)
+    }
+
+    pub fn right_binary_op(
+        &self,
+        op_slot: PyNumberBinaryOp,
+    ) -> PyResult<Option<PyNumberBinaryFunc>> {
+        use PyNumberBinaryOp::*;
+        let binary_op = match op_slot {
+            Add => self.right_add.load(),
+            Subtract => self.right_subtract.load(),
+            Multiply => self.right_multiply.load(),
+            Remainder => self.right_remainder.load(),
+            Divmod => self.right_divmod.load(),
+            Power => self.right_power.load(),
+            Lshift => self.right_lshift.load(),
+            Rshift => self.right_rshift.load(),
+            And => self.right_and.load(),
+            Xor => self.right_xor.load(),
+            Or => self.right_or.load(),
+            FloorDivide => self.right_floor_divide.load(),
+            TrueDivide => self.right_true_divide.load(),
+            MatrixMultiply => self.right_matrix_multiply.load(),
+            _ => None,
+        };
+        Ok(binary_op)
+    }
+}
+
 bitflags! {
     #[non_exhaustive]
     pub struct PyTypeFlags: u64 {
@@ -214,6 +336,18 @@ fn float_wrapper(num: PyNumber, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>>
     })
 }
 
+macro_rules! number_binary_op_wrapper {
+    ($name:ident) => {
+        |num, other, vm| {
+            vm.call_special_method(
+                num.obj.to_owned(),
+                identifier!(vm, $name),
+                (other.to_owned(),),
+            )
+        }
+    };
+}
+
 fn getitem_wrapper<K: ToPyObject>(obj: &PyObject, needle: K, vm: &VirtualMachine) -> PyResult {
     vm.call_special_method(obj.to_owned(), identifier!(vm, __getitem__), (needle,))
 }
@@ -361,6 +495,15 @@ impl PyType {
             }};
         }
 
+        macro_rules! toggle_subslot {
+            ($group:ident, $name:ident, $func:expr) => {
+                self.slots
+                    .$group
+                    .$name
+                    .store(if ADD { Some($func) } else { None });
+            };
+        }
+
         macro_rules! update_slot {
             ($name:ident, $func:expr) => {{
                 self.slots.$name.store(Some($func));
@@ -473,16 +616,188 @@ impl PyType {
                 toggle_slot!(del, del_wrapper);
             }
             _ if name == identifier!(ctx, __int__) => {
-                toggle_ext_func!(number_methods, int, int_wrapper);
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, int, int_wrapper);
             }
             _ if name == identifier!(ctx, __index__) => {
-                toggle_ext_func!(number_methods, index, index_wrapper);
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, index, index_wrapper);
             }
             _ if name == identifier!(ctx, __float__) => {
-                toggle_ext_func!(number_methods, float, float_wrapper);
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, float, float_wrapper);
+            }
+            _ if name == identifier!(ctx, __add__) => {
+                toggle_subslot!(number, add, number_binary_op_wrapper!(__add__));
+            }
+            _ if name == identifier!(ctx, __radd__) => {
+                toggle_subslot!(number, right_add, number_binary_op_wrapper!(__radd__));
+            }
+            _ if name == identifier!(ctx, __iadd__) => {
+                toggle_subslot!(number, inplace_add, number_binary_op_wrapper!(__iadd__));
+            }
+            _ if name == identifier!(ctx, __sub__) => {
+                toggle_subslot!(number, subtract, number_binary_op_wrapper!(__sub__));
+            }
+            _ if name == identifier!(ctx, __rsub__) => {
+                toggle_subslot!(number, right_subtract, number_binary_op_wrapper!(__rsub__));
+            }
+            _ if name == identifier!(ctx, __isub__) => {
+                toggle_subslot!(
+                    number,
+                    inplace_subtract,
+                    number_binary_op_wrapper!(__isub__)
+                );
+            }
+            _ if name == identifier!(ctx, __mul__) => {
+                toggle_subslot!(number, multiply, number_binary_op_wrapper!(__mul__));
+            }
+            _ if name == identifier!(ctx, __rmul__) => {
+                toggle_subslot!(number, right_multiply, number_binary_op_wrapper!(__rmul__));
+            }
+            _ if name == identifier!(ctx, __imul__) => {
+                toggle_subslot!(
+                    number,
+                    inplace_multiply,
+                    number_binary_op_wrapper!(__imul__)
+                );
+            }
+            _ if name == identifier!(ctx, __mod__) => {
+                toggle_subslot!(number, remainder, number_binary_op_wrapper!(__mod__));
+            }
+            _ if name == identifier!(ctx, __rmod__) => {
+                toggle_subslot!(number, right_remainder, number_binary_op_wrapper!(__rmod__));
+            }
+            _ if name == identifier!(ctx, __imod__) => {
+                toggle_subslot!(
+                    number,
+                    inplace_remainder,
+                    number_binary_op_wrapper!(__imod__)
+                );
+            }
+            _ if name == identifier!(ctx, __divmod__) => {
+                toggle_subslot!(number, divmod, number_binary_op_wrapper!(__divmod__));
+            }
+            _ if name == identifier!(ctx, __rdivmod__) => {
+                toggle_subslot!(number, right_divmod, number_binary_op_wrapper!(__rdivmod__));
+            }
+            _ if name == identifier!(ctx, __pow__) => {
+                toggle_subslot!(number, power, number_binary_op_wrapper!(__pow__));
+            }
+            _ if name == identifier!(ctx, __rpow__) => {
+                toggle_subslot!(number, right_power, number_binary_op_wrapper!(__rpow__));
+            }
+            _ if name == identifier!(ctx, __ipow__) => {
+                toggle_subslot!(number, inplace_power, number_binary_op_wrapper!(__ipow__));
+            }
+            _ if name == identifier!(ctx, __lshift__) => {
+                toggle_subslot!(number, lshift, number_binary_op_wrapper!(__lshift__));
+            }
+            _ if name == identifier!(ctx, __rlshift__) => {
+                toggle_subslot!(number, right_lshift, number_binary_op_wrapper!(__rlshift__));
+            }
+            _ if name == identifier!(ctx, __ilshift__) => {
+                toggle_subslot!(
+                    number,
+                    inplace_lshift,
+                    number_binary_op_wrapper!(__ilshift__)
+                );
+            }
+            _ if name == identifier!(ctx, __rshift__) => {
+                toggle_subslot!(number, rshift, number_binary_op_wrapper!(__rshift__));
+            }
+            _ if name == identifier!(ctx, __rrshift__) => {
+                toggle_subslot!(number, right_rshift, number_binary_op_wrapper!(__rrshift__));
+            }
+            _ if name == identifier!(ctx, __irshift__) => {
+                toggle_subslot!(
+                    number,
+                    inplace_rshift,
+                    number_binary_op_wrapper!(__irshift__)
+                );
+            }
+            _ if name == identifier!(ctx, __and__) => {
+                toggle_subslot!(number, and, number_binary_op_wrapper!(__and__));
+            }
+            _ if name == identifier!(ctx, __rand__) => {
+                toggle_subslot!(number, right_and, number_binary_op_wrapper!(__rand__));
+            }
+            _ if name == identifier!(ctx, __iand__) => {
+                toggle_subslot!(number, inplace_and, number_binary_op_wrapper!(__iand__));
+            }
+            _ if name == identifier!(ctx, __xor__) => {
+                toggle_subslot!(number, xor, number_binary_op_wrapper!(__xor__));
+            }
+            _ if name == identifier!(ctx, __rxor__) => {
+                toggle_subslot!(number, right_xor, number_binary_op_wrapper!(__rxor__));
+            }
+            _ if name == identifier!(ctx, __ixor__) => {
+                toggle_subslot!(number, inplace_xor, number_binary_op_wrapper!(__ixor__));
+            }
+            _ if name == identifier!(ctx, __or__) => {
+                toggle_subslot!(number, or, number_binary_op_wrapper!(__or__));
+            }
+            _ if name == identifier!(ctx, __ror__) => {
+                toggle_subslot!(number, right_or, number_binary_op_wrapper!(__ror__));
+            }
+            _ if name == identifier!(ctx, __ior__) => {
+                toggle_subslot!(number, inplace_or, number_binary_op_wrapper!(__ior__));
+            }
+            _ if name == identifier!(ctx, __floordiv__) => {
+                toggle_subslot!(
+                    number,
+                    floor_divide,
+                    number_binary_op_wrapper!(__floordiv__)
+                );
+            }
+            _ if name == identifier!(ctx, __rfloordiv__) => {
+                toggle_subslot!(
+                    number,
+                    right_floor_divide,
+                    number_binary_op_wrapper!(__rfloordiv__)
+                );
+            }
+            _ if name == identifier!(ctx, __ifloordiv__) => {
+                toggle_subslot!(
+                    number,
+                    inplace_floor_divide,
+                    number_binary_op_wrapper!(__ifloordiv__)
+                );
+            }
+            _ if name == identifier!(ctx, __truediv__) => {
+                toggle_subslot!(number, true_divide, number_binary_op_wrapper!(__truediv__));
+            }
+            _ if name == identifier!(ctx, __rtruediv__) => {
+                toggle_subslot!(
+                    number,
+                    right_true_divide,
+                    number_binary_op_wrapper!(__rtruediv__)
+                );
+            }
+            _ if name == identifier!(ctx, __itruediv__) => {
+                toggle_subslot!(
+                    number,
+                    inplace_true_divide,
+                    number_binary_op_wrapper!(__itruediv__)
+                );
+            }
+            _ if name == identifier!(ctx, __matmul__) => {
+                toggle_subslot!(
+                    number,
+                    matrix_multiply,
+                    number_binary_op_wrapper!(__matmul__)
+                );
+            }
+            _ if name == identifier!(ctx, __rmatmul__) => {
+                toggle_subslot!(
+                    number,
+                    right_matrix_multiply,
+                    number_binary_op_wrapper!(__rmatmul__)
+                );
+            }
+            _ if name == identifier!(ctx, __imatmul__) => {
+                toggle_subslot!(
+                    number,
+                    inplace_matrix_multiply,
+                    number_binary_op_wrapper!(__imatmul__)
+                );
             }
             _ => {}
         }
@@ -961,6 +1276,26 @@ pub trait AsSequence: PyPayload {
     }
 }
 
+macro_rules! extend_number_slot {
+    ($slots:ident, $methods:ident, $method:ident, $right_method:ident, $op_slot:ident) => {
+        if $methods.$method.is_some() {
+            $slots.number.$method.store($methods.$method);
+            $slots.number.$right_method.store(Some(|num, other, vm| {
+                num.methods.binary_op(PyNumberBinaryOp::$op_slot).unwrap()(
+                    other.to_number(),
+                    num.obj,
+                    vm,
+                )
+            }));
+        }
+    };
+    ($slots:ident, $methods:ident, $method:ident) => {
+        if $methods.$method.is_some() {
+            $slots.number.$method.store($methods.$method);
+        }
+    };
+}
+
 #[pyclass]
 pub trait AsNumber: PyPayload {
     #[pyslot]
@@ -984,6 +1319,60 @@ pub trait AsNumber: PyPayload {
             Self::clone_exact(Self::number_downcast(number), vm)
         }
     }
+
+    fn extend_slots(slots: &mut PyTypeSlots) {
+        let methods = Self::as_number();
+
+        extend_number_slot!(slots, methods, add, right_add, Add);
+        extend_number_slot!(slots, methods, subtract, right_subtract, Subtract);
+        extend_number_slot!(slots, methods, multiply, right_multiply, Multiply);
+        extend_number_slot!(slots, methods, remainder, right_remainder, Remainder);
+        extend_number_slot!(slots, methods, divmod, right_divmod, Divmod);
+        extend_number_slot!(slots, methods, power, right_power, Power);
+        extend_number_slot!(slots, methods, lshift, right_lshift, Lshift);
+        extend_number_slot!(slots, methods, rshift, right_rshift, Rshift);
+        extend_number_slot!(slots, methods, and, right_and, And);
+        extend_number_slot!(slots, methods, xor, right_xor, Xor);
+        extend_number_slot!(slots, methods, or, right_or, Or);
+        extend_number_slot!(
+            slots,
+            methods,
+            floor_divide,
+            right_floor_divide,
+            FloorDivide
+        );
+        extend_number_slot!(slots, methods, true_divide, right_true_divide, TrueDivide);
+        extend_number_slot!(
+            slots,
+            methods,
+            matrix_multiply,
+            right_matrix_multiply,
+            MatrixMultiply
+        );
+
+        extend_number_slot!(slots, methods, negative);
+        extend_number_slot!(slots, methods, positive);
+        extend_number_slot!(slots, methods, absolute);
+        extend_number_slot!(slots, methods, boolean);
+        extend_number_slot!(slots, methods, invert);
+        extend_number_slot!(slots, methods, int);
+        extend_number_slot!(slots, methods, float);
+        extend_number_slot!(slots, methods, index);
+
+        extend_number_slot!(slots, methods, inplace_add);
+        extend_number_slot!(slots, methods, inplace_subtract);
+        extend_number_slot!(slots, methods, inplace_multiply);
+        extend_number_slot!(slots, methods, inplace_remainder);
+        extend_number_slot!(slots, methods, inplace_power);
+        extend_number_slot!(slots, methods, inplace_lshift);
+        extend_number_slot!(slots, methods, inplace_rshift);
+        extend_number_slot!(slots, methods, inplace_and);
+        extend_number_slot!(slots, methods, inplace_xor);
+        extend_number_slot!(slots, methods, inplace_or);
+        extend_number_slot!(slots, methods, inplace_floor_divide);
+        extend_number_slot!(slots, methods, inplace_true_divide);
+        extend_number_slot!(slots, methods, inplace_matrix_multiply);
+    }
 }
 
 #[pyclass]
diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs
index 9db628f76d..95b8da260d 100644
--- a/vm/src/vm/context.rs
+++ b/vm/src/vm/context.rs
@@ -78,6 +78,7 @@ declare_const_name! {
     __aenter__,
     __aexit__,
     __aiter__,
+    __alloc__,
     __all__,
     __and__,
     __anext__,
@@ -121,7 +122,9 @@ declare_const_name! {
     __get__,
     __getattr__,
     __getattribute__,
+    __getformat__,
     __getitem__,
+    __getnewargs__,
     __gt__,
     __hash__,
     __iadd__,
@@ -146,6 +149,7 @@ declare_const_name! {
     __iter__,
     __itruediv__,
     __ixor__,
+    __jit__,  // RustPython dialect
     __le__,
     __len__,
     __length_hint__,
@@ -195,13 +199,16 @@ declare_const_name! {
     __rtruediv__,
     __rxor__,
     __set__,
-    __set_name__,
     __setattr__,
     __setitem__,
+    __setstate__,
+    __set_name__,
     __slots__,
     __str__,
     __sub__,
     __subclasscheck__,
+    __subclasshook__,
+    __subclasses__,
     __sizeof__,
     __truediv__,
     __trunc__,
diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs
index c0530a6916..ccc77b4409 100644
--- a/vm/src/vm/vm_ops.rs
+++ b/vm/src/vm/vm_ops.rs
@@ -1,11 +1,33 @@
 use super::{PyMethod, VirtualMachine};
 use crate::{
-    builtins::{PyInt, PyIntRef, PyStr, PyStrInterned, PyStrRef},
-    function::PyArithmeticValue,
+    builtins::{PyInt, PyIntRef, PyStr, PyStrRef},
     object::{AsObject, PyObject, PyObjectRef, PyResult},
-    protocol::PyIterReturn,
+    protocol::{PyIterReturn, PyNumberBinaryOp, PySequence},
     types::PyComparisonOp,
 };
+use num_traits::ToPrimitive;
+
+macro_rules! binary_func {
+    ($fn:ident, $op_slot:ident, $op:expr) => {
+        pub fn $fn(&self, a: &PyObject, b: &PyObject) -> PyResult {
+            self.binary_op(a, b, PyNumberBinaryOp::$op_slot, $op)
+        }
+    };
+}
+
+macro_rules! inplace_binary_func {
+    ($fn:ident, $iop_slot:ident, $op_slot:ident, $op:expr) => {
+        pub fn $fn(&self, a: &PyObject, b: &PyObject) -> PyResult {
+            self.binary_iop(
+                a,
+                b,
+                PyNumberBinaryOp::$iop_slot,
+                PyNumberBinaryOp::$op_slot,
+                $op,
+            )
+        }
+    };
+}
 
 /// Collection of operators
 impl VirtualMachine {
@@ -103,371 +125,212 @@ impl VirtualMachine {
         }
     }
 
-    /// Calls a method on `obj` passing `arg`, if the method exists.
+    /// Calling scheme used for binary operations:
     ///
-    /// Otherwise, or if the result is the special `NotImplemented` built-in constant,
-    /// calls `unsupported` to determine fallback value.
-    pub fn call_or_unsupported<F>(
-        &self,
-        obj: &PyObject,
-        arg: &PyObject,
-        method: &'static PyStrInterned,
-        unsupported: F,
-    ) -> PyResult
-    where
-        F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult,
-    {
-        if let Some(method_or_err) = self.get_method(obj.to_owned(), method) {
-            let method = method_or_err?;
-            let result = method.call((arg.to_owned(),), self)?;
-            if let PyArithmeticValue::Implemented(x) = PyArithmeticValue::from_object(self, result)
-            {
+    /// Order operations are tried until either a valid result or error:
+    ///   b.rop(b,a)[*], a.op(a,b), b.rop(b,a)
+    ///
+    /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a)
+    pub fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: PyNumberBinaryOp) -> PyResult {
+        let slot_a = a.class().slots.number.left_binary_op(op_slot)?;
+        let mut slot_b = if b.class().is(a.class()) {
+            None
+        } else {
+            match b.class().slots.number.right_binary_op(op_slot)? {
+                Some(slot_b)
+                    if slot_b as usize == slot_a.map(|s| s as usize).unwrap_or_default() =>
+                {
+                    None
+                }
+                slot_b => slot_b,
+            }
+        };
+
+        if let Some(slot_a) = slot_a {
+            if let Some(slot_bb) = slot_b {
+                if b.fast_isinstance(a.class()) {
+                    let x = slot_bb(b.to_number(), a, self)?;
+                    if !x.is(&self.ctx.not_implemented) {
+                        return Ok(x);
+                    }
+                    slot_b = None;
+                }
+            }
+            let x = slot_a(a.to_number(), b, self)?;
+            if !x.is(&self.ctx.not_implemented) {
                 return Ok(x);
             }
         }
-        unsupported(self, obj, arg)
+
+        if let Some(slot_b) = slot_b {
+            let x = slot_b(b.to_number(), a, self)?;
+            if !x.is(&self.ctx.not_implemented) {
+                return Ok(x);
+            }
+        }
+
+        Ok(self.ctx.not_implemented())
     }
 
-    /// Calls a method, falling back to its reflection with the operands
-    /// reversed, and then to the value provided by `unsupported`.
+    pub fn binary_op(
+        &self,
+        a: &PyObject,
+        b: &PyObject,
+        op_slot: PyNumberBinaryOp,
+        op: &str,
+    ) -> PyResult {
+        let result = self.binary_op1(a, b, op_slot)?;
+        if !result.is(&self.ctx.not_implemented) {
+            return Ok(result);
+        }
+        Err(self.new_unsupported_binop_error(a, b, op))
+    }
+
+    /// Binary in-place operators
     ///
-    /// For example: the following:
+    /// The in-place operators are defined to fall back to the 'normal',
+    /// non in-place operations, if the in-place methods are not in place.
     ///
-    /// `call_or_reflection(lhs, rhs, "__and__", "__rand__", unsupported)`
+    /// - If the left hand object has the appropriate struct members, and
+    ///   they are filled, call the appropriate function and return the
+    ///   result.  No coercion is done on the arguments; the left-hand object
+    ///   is the one the operation is performed on, and it's up to the
+    ///   function to deal with the right-hand object.
     ///
-    /// 1. Calls `__and__` with `lhs` and `rhs`.
-    /// 2. If above is not implemented, calls `__rand__` with `rhs` and `lhs`.
-    /// 3. If above is not implemented, invokes `unsupported` for the result.
-    pub fn call_or_reflection(
+    /// - Otherwise, in-place modification is not supported. Handle it exactly as
+    ///   a non in-place operation of the same kind.
+    fn binary_iop1(
         &self,
-        lhs: &PyObject,
-        rhs: &PyObject,
-        default: &'static PyStrInterned,
-        reflection: &'static PyStrInterned,
-        unsupported: fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult,
+        a: &PyObject,
+        b: &PyObject,
+        iop_slot: PyNumberBinaryOp,
+        op_slot: PyNumberBinaryOp,
     ) -> PyResult {
-        if rhs.fast_isinstance(lhs.class()) {
-            let lop = lhs.get_class_attr(reflection);
-            let rop = rhs.get_class_attr(reflection);
-            if let Some((lop, rop)) = lop.zip(rop) {
-                if !lop.is(&rop) {
-                    if let Ok(r) = self.call_or_unsupported(rhs, lhs, reflection, |vm, _, _| {
-                        Err(vm.new_exception_empty(vm.ctx.exceptions.exception_type.to_owned()))
-                    }) {
-                        return Ok(r);
-                    }
-                }
+        if let Some(slot) = a.class().slots.number.left_binary_op(iop_slot)? {
+            let x = slot(a.to_number(), b, self)?;
+            if !x.is(&self.ctx.not_implemented) {
+                return Ok(x);
             }
         }
-        // Try to call the default method
-        self.call_or_unsupported(lhs, rhs, default, move |vm, lhs, rhs| {
-            // Try to call the reflection method
-            // don't call reflection method if operands are of the same type
-            if !lhs.class().is(rhs.class()) {
-                vm.call_or_unsupported(rhs, lhs, reflection, |_, rhs, lhs| {
-                    // switch them around again
-                    unsupported(vm, lhs, rhs)
-                })
-            } else {
-                unsupported(vm, lhs, rhs)
-            }
-        })
-    }
-
-    pub fn _sub(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __sub__),
-            identifier!(self, __rsub__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "-")),
-        )
+        self.binary_op1(a, b, op_slot)
     }
 
-    pub fn _isub(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __isub__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __sub__),
-                identifier!(self, __rsub__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "-=")),
-            )
-        })
-    }
+    fn binary_iop(
+        &self,
+        a: &PyObject,
+        b: &PyObject,
+        iop_slot: PyNumberBinaryOp,
+        op_slot: PyNumberBinaryOp,
+        op: &str,
+    ) -> PyResult {
+        let result = self.binary_iop1(a, b, iop_slot, op_slot)?;
+        if !result.is(&self.ctx.not_implemented) {
+            return Ok(result);
+        }
+        Err(self.new_unsupported_binop_error(a, b, op))
+    }
+
+    binary_func!(_sub, Subtract, "-");
+    binary_func!(_mod, Remainder, "%");
+    binary_func!(_divmod, Divmod, "divmod");
+    binary_func!(_pow, Power, "**");
+    binary_func!(_lshift, Lshift, "<<");
+    binary_func!(_rshift, Rshift, ">>");
+    binary_func!(_and, And, "&");
+    binary_func!(_xor, Xor, "^");
+    binary_func!(_or, Or, "|");
+    binary_func!(_floordiv, FloorDivide, "//");
+    binary_func!(_truediv, TrueDivide, "/");
+    binary_func!(_matmul, MatrixMultiply, "@");
+
+    inplace_binary_func!(_isub, InplaceSubtract, Subtract, "-=");
+    inplace_binary_func!(_imod, InplaceRemainder, Remainder, "%=");
+    inplace_binary_func!(_ipow, InplacePower, Power, "**=");
+    inplace_binary_func!(_ilshift, InplaceLshift, Lshift, "<<=");
+    inplace_binary_func!(_irshift, InplaceRshift, Rshift, ">>=");
+    inplace_binary_func!(_iand, InplaceAnd, And, "&=");
+    inplace_binary_func!(_ixor, InplaceXor, Xor, "^=");
+    inplace_binary_func!(_ior, InplaceOr, Or, "|=");
+    inplace_binary_func!(_ifloordiv, InplaceFloorDivide, FloorDivide, "//=");
+    inplace_binary_func!(_itruediv, InplaceTrueDivide, TrueDivide, "/=");
+    inplace_binary_func!(_imatmul, InplaceMatrixMultiply, MatrixMultiply, "@=");
 
     pub fn _add(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __add__),
-            identifier!(self, __radd__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+")),
-        )
+        let result = self.binary_op1(a, b, PyNumberBinaryOp::Add)?;
+        if !result.is(&self.ctx.not_implemented) {
+            return Ok(result);
+        }
+        if let Ok(seq_a) = PySequence::try_protocol(a, self) {
+            let result = seq_a.concat(b, self)?;
+            if !result.is(&self.ctx.not_implemented) {
+                return Ok(result);
+            }
+        }
+        Err(self.new_unsupported_binop_error(a, b, "+"))
     }
 
     pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __iadd__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __add__),
-                identifier!(self, __radd__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+=")),
-            )
-        })
+        let result = self.binary_iop1(a, b, PyNumberBinaryOp::InplaceAdd, PyNumberBinaryOp::Add)?;
+        if !result.is(&self.ctx.not_implemented) {
+            return Ok(result);
+        }
+        if let Ok(seq_a) = PySequence::try_protocol(a, self) {
+            let result = seq_a.inplace_concat(b, self)?;
+            if !result.is(&self.ctx.not_implemented) {
+                return Ok(result);
+            }
+        }
+        Err(self.new_unsupported_binop_error(a, b, "+="))
     }
 
     pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __mul__),
-            identifier!(self, __rmul__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*")),
-        )
+        let result = self.binary_op1(a, b, PyNumberBinaryOp::Multiply)?;
+        if !result.is(&self.ctx.not_implemented) {
+            return Ok(result);
+        }
+        if let Ok(seq_a) = PySequence::try_protocol(a, self) {
+            let n =
+                b.try_index(self)?.as_bigint().to_isize().ok_or_else(|| {
+                    self.new_overflow_error("repeated bytes are too long".to_owned())
+                })?;
+            return seq_a.repeat(n, self);
+        } else if let Ok(seq_b) = PySequence::try_protocol(b, self) {
+            let n =
+                a.try_index(self)?.as_bigint().to_isize().ok_or_else(|| {
+                    self.new_overflow_error("repeated bytes are too long".to_owned())
+                })?;
+            return seq_b.repeat(n, self);
+        }
+        Err(self.new_unsupported_binop_error(a, b, "*"))
     }
 
     pub fn _imul(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __imul__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __mul__),
-                identifier!(self, __rmul__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*=")),
-            )
-        })
-    }
-
-    pub fn _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __matmul__),
-            identifier!(self, __rmatmul__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@")),
-        )
-    }
-
-    pub fn _imatmul(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __imatmul__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __matmul__),
-                identifier!(self, __rmatmul__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@=")),
-            )
-        })
-    }
-
-    pub fn _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __truediv__),
-            identifier!(self, __rtruediv__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/")),
-        )
-    }
-
-    pub fn _itruediv(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __itruediv__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __truediv__),
-                identifier!(self, __rtruediv__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/=")),
-            )
-        })
-    }
-
-    pub fn _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __floordiv__),
-            identifier!(self, __rfloordiv__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//")),
-        )
-    }
-
-    pub fn _ifloordiv(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __ifloordiv__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __floordiv__),
-                identifier!(self, __rfloordiv__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//=")),
-            )
-        })
-    }
-
-    pub fn _pow(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __pow__),
-            identifier!(self, __rpow__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**")),
-        )
-    }
-
-    pub fn _ipow(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __ipow__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __pow__),
-                identifier!(self, __rpow__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**=")),
-            )
-        })
-    }
-
-    pub fn _mod(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __mod__),
-            identifier!(self, __rmod__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%")),
-        )
-    }
-
-    pub fn _imod(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __imod__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __mod__),
-                identifier!(self, __rmod__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%=")),
-            )
-        })
-    }
-
-    pub fn _divmod(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __divmod__),
-            identifier!(self, __rdivmod__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "divmod")),
-        )
-    }
-
-    pub fn _lshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __lshift__),
-            identifier!(self, __rlshift__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<")),
-        )
-    }
-
-    pub fn _ilshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __ilshift__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __lshift__),
-                identifier!(self, __rlshift__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<=")),
-            )
-        })
-    }
-
-    pub fn _rshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __rshift__),
-            identifier!(self, __rrshift__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>")),
-        )
-    }
-
-    pub fn _irshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __irshift__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __rshift__),
-                identifier!(self, __rrshift__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>=")),
-            )
-        })
-    }
-
-    pub fn _xor(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
+        let result = self.binary_iop1(
             a,
             b,
-            identifier!(self, __xor__),
-            identifier!(self, __rxor__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^")),
-        )
-    }
-
-    pub fn _ixor(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __ixor__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __xor__),
-                identifier!(self, __rxor__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^=")),
-            )
-        })
-    }
-
-    pub fn _or(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __or__),
-            identifier!(self, __ror__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|")),
-        )
-    }
-
-    pub fn _ior(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __ior__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __or__),
-                identifier!(self, __ror__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|=")),
-            )
-        })
-    }
-
-    pub fn _and(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_reflection(
-            a,
-            b,
-            identifier!(self, __and__),
-            identifier!(self, __rand__),
-            |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&")),
-        )
-    }
-
-    pub fn _iand(&self, a: &PyObject, b: &PyObject) -> PyResult {
-        self.call_or_unsupported(a, b, identifier!(self, __iand__), |vm, a, b| {
-            vm.call_or_reflection(
-                a,
-                b,
-                identifier!(self, __and__),
-                identifier!(self, __rand__),
-                |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&=")),
-            )
-        })
+            PyNumberBinaryOp::InplaceMultiply,
+            PyNumberBinaryOp::Multiply,
+        )?;
+        if !result.is(&self.ctx.not_implemented) {
+            return Ok(result);
+        }
+        if let Ok(seq_a) = PySequence::try_protocol(a, self) {
+            let n =
+                b.try_index(self)?.as_bigint().to_isize().ok_or_else(|| {
+                    self.new_overflow_error("repeated bytes are too long".to_owned())
+                })?;
+            return seq_a.inplace_repeat(n, self);
+        } else if let Ok(seq_b) = PySequence::try_protocol(b, self) {
+            let n =
+                a.try_index(self)?.as_bigint().to_isize().ok_or_else(|| {
+                    self.new_overflow_error("repeated bytes are too long".to_owned())
+                })?;
+            /* Note that the right hand operand should not be
+             * mutated in this case so inplace_repeat is not
+             * used. */
+            return seq_b.repeat(n, self);
+        }
+        Err(self.new_unsupported_binop_error(a, b, "*="))
     }
 
     pub fn _abs(&self, a: &PyObject) -> PyResult<PyObjectRef> {


Test patch:
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 68dee5ce67..c8f6880d09 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -259,6 +259,8 @@ def __contains__(self, key):
         d = c.new_child(b=20, c=30)
         self.assertEqual(d.maps, [{'b': 20, 'c': 30}, {'a': 1, 'b': 2}])
 
+    # TODO: RUSTPYTHON
+    @unittest.expectedFailure
     def test_union_operators(self):
         cm1 = ChainMap(dict(a=1, b=2), dict(c=3, d=4))
         cm2 = ChainMap(dict(a=10, e=5), dict(b=20, d=4))
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 19273e9965..0c640c2536 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -2466,9 +2466,7 @@ class Color(StrMixin, AllMixin, Flag):
         self.assertEqual(Color.ALL.value, 7)
         self.assertEqual(str(Color.BLUE), 'blue')
 
-    # TODO: RUSTPYTHON
-    @unittest.expectedFailure
-    @unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON, inconsistent test result on Windows due to threading")
+    @unittest.skip("TODO: RUSTPYTHON, inconsistent test result on Windows due to threading")
     @threading_helper.reap_threads
     def test_unique_composite(self):
         # override __eq__ to be identity only
diff --git a/Lib/test/test_xml_dom_minicompat.py b/Lib/test/test_xml_dom_minicompat.py
index e2931256ab..c90a01d2e4 100644
--- a/Lib/test/test_xml_dom_minicompat.py
+++ b/Lib/test/test_xml_dom_minicompat.py
@@ -35,8 +35,6 @@ def test_emptynodelist___add__(self):
         node_list = EmptyNodeList() + NodeList()
         self.assertEqual(node_list, NodeList())
 
-    # TODO: RUSTPYTHON
-    @unittest.expectedFailure
     def test_emptynodelist___radd__(self):
         node_list = [1,2] + EmptyNodeList()
         self.assertEqual(node_list, [1,2])