diff --git a/CHANGELOG.md b/CHANGELOG.md
index c15cc0b03..9b748cf6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
 # Changelog
 - v0.23.0
   - Drop support for PyPy 3.7 and 3.8. ([#470](https://github.com/PyO3/rust-numpy/pull/470))
+  - Require `Element: Sync` as part of the free-threading support in PyO3 0.23 ([#469](https://github.com/PyO3/rust-numpy/pull/469))
+  - Bump PyO3 dependency to v0.23.0 ([[#457](https://github.com/PyO3/rust-numpy/pull/457)])
+    - removed the `gil-refs` feature
+    - reintroduced function names without `_bound` suffix + deprecating the old names
+    - switched to `IntoPyObject` as trait bound
+
 - v0.22.1
   - Fix building on 32-bit Windows. ([#463](https://github.com/PyO3/rust-numpy/pull/463))
   - Add `PyReadwriteArray::make_nonwriteable`. ([#462](https://github.com/PyO3/rust-numpy/pull/462))
diff --git a/Cargo.toml b/Cargo.toml
index 5c9e852c9..90c74f452 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "numpy"
-version = "0.22.1"
+version = "0.23.0-dev"
 authors = [
     "The rust-numpy Project Developers",
     "PyO3 Project and Contributors <https://github.com/PyO3>"
@@ -22,15 +22,12 @@ num-complex = ">= 0.2, < 0.5"
 num-integer = "0.1"
 num-traits = "0.2"
 ndarray = ">= 0.15, < 0.17"
-pyo3 = { version = "0.22.0", default-features = false, features = ["macros"] }
+pyo3 = { version = "0.23.0", default-features = false, features = ["macros"] }
 rustc-hash = "1.1"
 
 [dev-dependencies]
-pyo3 = { version = "0.22.0", default-features = false, features = ["auto-initialize"] }
+pyo3 = { version = "0.23.0", default-features = false, features = ["auto-initialize"] }
 nalgebra = { version = ">=0.30, <0.34", default-features = false, features = ["std"] }
 
 [package.metadata.docs.rs]
 all-features = true
-
-[features]
-gil-refs = ["pyo3/gil-refs"]
diff --git a/README.md b/README.md
index 80945c746..32e72eebd 100644
--- a/README.md
+++ b/README.md
@@ -71,7 +71,7 @@ fn rust_ext<'py>(_py: Python<'py>, m: &Bound<'py, PyModule>) -> PyResult<()> {
         let x = x.as_array();
         let y = y.as_array();
         let z = axpy(a, x, y);
-        z.into_pyarray_bound(py)
+        z.into_pyarray(py)
     }
 
     // wrapper of `mult`
@@ -99,15 +99,15 @@ numpy = "0.22"
 
 ```rust
 use numpy::{PyArray1, PyArrayMethods};
-use pyo3::{types::{IntoPyDict, PyAnyMethods}, PyResult, Python};
+use pyo3::{types::{IntoPyDict, PyAnyMethods}, PyResult, Python, ffi::c_str};
 
 fn main() -> PyResult<()> {
     Python::with_gil(|py| {
-        let np = py.import_bound("numpy")?;
-        let locals = [("np", np)].into_py_dict_bound(py);
+        let np = py.import("numpy")?;
+        let locals = [("np", np)].into_py_dict(py)?;
 
         let pyarray = py
-            .eval_bound("np.absolute(np.array([-1, -2, -3], dtype='int32'))", Some(&locals), None)?
+            .eval(c_str!("np.absolute(np.array([-1, -2, -3], dtype='int32'))"), Some(&locals), None)?
             .downcast_into::<PyArray1<i32>>()?;
 
         let readonly = pyarray.readonly();
diff --git a/benches/array.rs b/benches/array.rs
index 56d4bd117..3df2321bb 100644
--- a/benches/array.rs
+++ b/benches/array.rs
@@ -6,12 +6,12 @@ use test::{black_box, Bencher};
 use std::ops::Range;
 
 use numpy::{PyArray1, PyArray2, PyArray3};
-use pyo3::{types::PyAnyMethods, Bound, Python, ToPyObject};
+use pyo3::{types::PyAnyMethods, Bound, Python};
 
 #[bench]
 fn extract_success(bencher: &mut Bencher) {
     Python::with_gil(|py| {
-        let any = PyArray2::<f64>::zeros_bound(py, (10, 10), false).into_any();
+        let any = PyArray2::<f64>::zeros(py, (10, 10), false).into_any();
 
         bencher.iter(|| {
             black_box(&any)
@@ -24,7 +24,7 @@ fn extract_success(bencher: &mut Bencher) {
 #[bench]
 fn extract_failure(bencher: &mut Bencher) {
     Python::with_gil(|py| {
-        let any = PyArray2::<f64>::zeros_bound(py, (10, 10), false).into_any();
+        let any = PyArray2::<f64>::zeros(py, (10, 10), false).into_any();
 
         bencher.iter(|| {
             black_box(&any)
@@ -37,7 +37,7 @@ fn extract_failure(bencher: &mut Bencher) {
 #[bench]
 fn downcast_success(bencher: &mut Bencher) {
     Python::with_gil(|py| {
-        let any = PyArray2::<f64>::zeros_bound(py, (10, 10), false).into_any();
+        let any = PyArray2::<f64>::zeros(py, (10, 10), false).into_any();
 
         bencher.iter(|| black_box(&any).downcast::<PyArray2<f64>>().unwrap());
     });
@@ -46,7 +46,7 @@ fn downcast_success(bencher: &mut Bencher) {
 #[bench]
 fn downcast_failure(bencher: &mut Bencher) {
     Python::with_gil(|py| {
-        let any = PyArray2::<f64>::zeros_bound(py, (10, 10), false).into_any();
+        let any = PyArray2::<f64>::zeros(py, (10, 10), false).into_any();
 
         bencher.iter(|| black_box(&any).downcast::<PyArray2<f64>>().unwrap_err());
     });
@@ -67,7 +67,7 @@ fn from_iter(bencher: &mut Bencher, size: usize) {
         bencher.iter(|| {
             let iter = black_box(Iter(0..size));
 
-            PyArray1::from_iter_bound(py, iter)
+            PyArray1::from_iter(py, iter)
         });
     });
 }
@@ -94,7 +94,7 @@ fn from_slice(bencher: &mut Bencher, size: usize) {
         bencher.iter(|| {
             let slice = black_box(&vec);
 
-            PyArray1::from_slice_bound(py, slice)
+            PyArray1::from_slice(py, slice)
         });
     });
 }
@@ -121,7 +121,7 @@ fn from_object_slice(bencher: &mut Bencher, size: usize) {
         bencher.iter(|| {
             let slice = black_box(&vec);
 
-            PyArray1::from_slice_bound(py, slice)
+            PyArray1::from_slice(py, slice)
         });
     });
 }
@@ -148,7 +148,7 @@ fn from_vec2(bencher: &mut Bencher, size: usize) {
         bencher.iter(|| {
             let vec2 = black_box(&vec2);
 
-            PyArray2::from_vec2_bound(py, vec2).unwrap()
+            PyArray2::from_vec2(py, vec2).unwrap()
         });
     });
 }
@@ -175,7 +175,7 @@ fn from_vec3(bencher: &mut Bencher, size: usize) {
         bencher.iter(|| {
             let vec3 = black_box(&vec3);
 
-            PyArray3::from_vec3_bound(py, vec3).unwrap()
+            PyArray3::from_vec3(py, vec3).unwrap()
         });
     });
 }
diff --git a/benches/borrow.rs b/benches/borrow.rs
index 79e7dc030..f7c0c7641 100644
--- a/benches/borrow.rs
+++ b/benches/borrow.rs
@@ -9,7 +9,7 @@ use pyo3::Python;
 #[bench]
 fn initial_shared_borrow(bencher: &mut Bencher) {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (6, 5, 4, 3, 2, 1), false);
+        let array = PyArray::<f64, _>::zeros(py, (6, 5, 4, 3, 2, 1), false);
 
         bencher.iter(|| {
             let array = black_box(&array);
@@ -22,7 +22,7 @@ fn initial_shared_borrow(bencher: &mut Bencher) {
 #[bench]
 fn additional_shared_borrow(bencher: &mut Bencher) {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (6, 5, 4, 3, 2, 1), false);
+        let array = PyArray::<f64, _>::zeros(py, (6, 5, 4, 3, 2, 1), false);
 
         let _shared = (0..128).map(|_| array.readonly()).collect::<Vec<_>>();
 
@@ -37,7 +37,7 @@ fn additional_shared_borrow(bencher: &mut Bencher) {
 #[bench]
 fn exclusive_borrow(bencher: &mut Bencher) {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (6, 5, 4, 3, 2, 1), false);
+        let array = PyArray::<f64, _>::zeros(py, (6, 5, 4, 3, 2, 1), false);
 
         bencher.iter(|| {
             let array = black_box(&array);
diff --git a/examples/linalg/Cargo.lock b/examples/linalg/Cargo.lock
index 2ad2745c3..2a6c81d79 100644
--- a/examples/linalg/Cargo.lock
+++ b/examples/linalg/Cargo.lock
@@ -209,9 +209,9 @@ dependencies = [
 
 [[package]]
 name = "heck"
-version = "0.4.1"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "idna"
@@ -285,16 +285,6 @@ version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
-[[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
 [[package]]
 name = "log"
 version = "0.4.22"
@@ -408,7 +398,7 @@ dependencies = [
 
 [[package]]
 name = "numpy"
-version = "0.21.0"
+version = "0.23.0-dev"
 dependencies = [
  "libc",
  "ndarray",
@@ -495,29 +485,6 @@ dependencies = [
  "vcpkg",
 ]
 
-[[package]]
-name = "parking_lot"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-targets",
-]
-
 [[package]]
 name = "percent-encoding"
 version = "2.3.1"
@@ -556,15 +523,15 @@ dependencies = [
 
 [[package]]
 name = "pyo3"
-version = "0.21.2"
+version = "0.23.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8"
+checksum = "7ebb0c0cc0de9678e53be9ccf8a2ab53045e6e3a8be03393ceccc5e7396ccb40"
 dependencies = [
  "cfg-if",
  "indoc",
  "libc",
  "memoffset",
- "parking_lot",
+ "once_cell",
  "portable-atomic",
  "pyo3-build-config",
  "pyo3-ffi",
@@ -574,9 +541,9 @@ dependencies = [
 
 [[package]]
 name = "pyo3-build-config"
-version = "0.21.2"
+version = "0.23.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50"
+checksum = "80e3ce69c4ec34476534b490e412b871ba03a82e35604c3dfb95fcb6bfb60c09"
 dependencies = [
  "once_cell",
  "target-lexicon",
@@ -584,9 +551,9 @@ dependencies = [
 
 [[package]]
 name = "pyo3-ffi"
-version = "0.21.2"
+version = "0.23.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403"
+checksum = "3b09f311c76b36dfd6dd6f7fa6f9f18e7e46a1c937110d283e80b12ba2468a75"
 dependencies = [
  "libc",
  "pyo3-build-config",
@@ -594,9 +561,9 @@ dependencies = [
 
 [[package]]
 name = "pyo3-macros"
-version = "0.21.2"
+version = "0.23.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c"
+checksum = "fd4f74086536d1e1deaff99ec0387481fb3325c82e4e48be0e75ab3d3fcb487a"
 dependencies = [
  "proc-macro2",
  "pyo3-macros-backend",
@@ -606,9 +573,9 @@ dependencies = [
 
 [[package]]
 name = "pyo3-macros-backend"
-version = "0.21.2"
+version = "0.23.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c"
+checksum = "9e77dfeb76b32bbf069144a5ea0a36176ab59c8db9ce28732d0f06f096bbfbc8"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -757,12 +724,6 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
 [[package]]
 name = "security-framework"
 version = "2.11.1"
@@ -812,12 +773,6 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
-[[package]]
-name = "smallvec"
-version = "1.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
-
 [[package]]
 name = "syn"
 version = "2.0.75"
diff --git a/examples/linalg/Cargo.toml b/examples/linalg/Cargo.toml
index fc4a3a29c..3faa3acd0 100644
--- a/examples/linalg/Cargo.toml
+++ b/examples/linalg/Cargo.toml
@@ -9,7 +9,7 @@ name = "rust_linalg"
 crate-type = ["cdylib"]
 
 [dependencies]
-pyo3 = { version = "0.22.0", features = ["extension-module"] }
+pyo3 = { version = "0.23.0", features = ["extension-module"] }
 numpy = { path = "../.." }
 ndarray-linalg = { version = "0.14.1", features = ["openblas-system"] }
 
diff --git a/examples/linalg/src/lib.rs b/examples/linalg/src/lib.rs
index 729c965e7..27b333edd 100755
--- a/examples/linalg/src/lib.rs
+++ b/examples/linalg/src/lib.rs
@@ -13,7 +13,7 @@ fn rust_linalg<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
         let y = x
             .inv()
             .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
-        Ok(y.into_pyarray_bound(py))
+        Ok(y.into_pyarray(py))
     }
     Ok(())
 }
diff --git a/examples/parallel/Cargo.toml b/examples/parallel/Cargo.toml
index af40e2fec..0ed28cb71 100644
--- a/examples/parallel/Cargo.toml
+++ b/examples/parallel/Cargo.toml
@@ -9,7 +9,7 @@ name = "rust_parallel"
 crate-type = ["cdylib"]
 
 [dependencies]
-pyo3 = { version = "0.22.0", features = ["extension-module", "multiple-pymethods"] }
+pyo3 = { version = "0.23.0", features = ["extension-module", "multiple-pymethods"] }
 numpy = { path = "../.." }
 ndarray = { version = "0.16", features = ["rayon", "blas"] }
 blas-src = { version = "0.8", features = ["openblas"] }
diff --git a/examples/parallel/src/lib.rs b/examples/parallel/src/lib.rs
index 20a04d331..b1adc1cae 100755
--- a/examples/parallel/src/lib.rs
+++ b/examples/parallel/src/lib.rs
@@ -16,7 +16,7 @@ fn rust_parallel<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
         let x = x.as_array();
         let y = y.as_array();
         let z = Zip::from(x.rows()).par_map_collect(|row| row.dot(&y));
-        z.into_pyarray_bound(py)
+        z.into_pyarray(py)
     }
     Ok(())
 }
diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml
index b3fd06bca..c180039f7 100644
--- a/examples/simple/Cargo.toml
+++ b/examples/simple/Cargo.toml
@@ -9,7 +9,7 @@ name = "rust_ext"
 crate-type = ["cdylib"]
 
 [dependencies]
-pyo3 = { version = "0.22.0", features = ["extension-module", "abi3-py37"] }
+pyo3 = { version = "0.23.0", features = ["extension-module", "abi3-py37"] }
 numpy = { path = "../.." }
 
 [workspace]
diff --git a/examples/simple/src/lib.rs b/examples/simple/src/lib.rs
index fa1c844c7..3bb29e3e1 100644
--- a/examples/simple/src/lib.rs
+++ b/examples/simple/src/lib.rs
@@ -64,7 +64,7 @@ fn rust_ext<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
         let x = x.as_array();
         let y = y.as_array();
         let z = axpy(a, x, y);
-        z.into_pyarray_bound(py)
+        z.into_pyarray(py)
     }
 
     // wrapper of `mult`
@@ -82,7 +82,7 @@ fn rust_ext<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
         py: Python<'py>,
         x: PyReadonlyArrayDyn<'py, Complex64>,
     ) -> Bound<'py, PyArrayDyn<Complex64>> {
-        conj(x.as_array()).into_pyarray_bound(py)
+        conj(x.as_array()).into_pyarray(py)
     }
 
     // example of how to extract an array from a dictionary
@@ -131,13 +131,13 @@ fn rust_ext<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
                 x.readonly().as_array(),
                 y.readonly().as_array(),
             )
-            .into_pyarray_bound(x.py())
+            .into_pyarray(x.py())
             .into_any()),
             (SupportedArray::I64(x), SupportedArray::I64(y)) => Ok(generic_add(
                 x.readonly().as_array(),
                 y.readonly().as_array(),
             )
-            .into_pyarray_bound(x.py())
+            .into_pyarray(x.py())
             .into_any()),
             (SupportedArray::F64(x), SupportedArray::I64(y))
             | (SupportedArray::I64(y), SupportedArray::F64(x)) => {
@@ -145,7 +145,7 @@ fn rust_ext<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
 
                 Ok(
                     generic_add(x.readonly().as_array(), y.readonly().as_array())
-                        .into_pyarray_bound(x.py())
+                        .into_pyarray(x.py())
                         .into_any(),
                 )
             }
diff --git a/src/array.rs b/src/array.rs
index cdaead703..81ad31045 100644
--- a/src/array.rs
+++ b/src/array.rs
@@ -5,7 +5,6 @@
 use std::{
     marker::PhantomData,
     mem,
-    ops::Deref,
     os::raw::{c_int, c_void},
     ptr, slice,
 };
@@ -17,13 +16,10 @@ use ndarray::{
 };
 use num_traits::AsPrimitive;
 use pyo3::{
-    ffi, pyobject_native_type_base,
+    ffi,
     types::{DerefToPyAny, PyAnyMethods, PyModule},
-    AsPyPointer, Bound, DowncastError, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, PyTypeInfo,
-    Python,
+    Bound, DowncastError, Py, PyAny, PyErr, PyObject, PyResult, PyTypeInfo, Python,
 };
-#[cfg(feature = "gil-refs")]
-use pyo3::{FromPyObject, PyNativeType};
 
 use crate::borrow::{PyReadonlyArray, PyReadwriteArray};
 use crate::cold;
@@ -88,7 +84,7 @@ use crate::untyped_array::{PyUntypedArray, PyUntypedArrayMethods};
 /// use pyo3::Python;
 ///
 /// Python::with_gil(|py| {
-///     let pyarray = PyArray::arange_bound(py, 0., 4., 1.).reshape([2, 2]).unwrap();
+///     let pyarray = PyArray::arange(py, 0., 4., 1.).reshape([2, 2]).unwrap();
 ///     let array = array![[3., 4.], [5., 6.]];
 ///
 ///     assert_eq!(
@@ -122,7 +118,7 @@ pub type PyArrayDyn<T> = PyArray<T, IxDyn>;
 
 /// Returns a handle to NumPy's multiarray module.
 pub fn get_array_module<'py>(py: Python<'py>) -> PyResult<Bound<'_, PyModule>> {
-    PyModule::import_bound(py, npyffi::array::mod_name(py)?)
+    PyModule::import(py, npyffi::array::mod_name(py)?)
 }
 
 impl<T, D> DerefToPyAny for PyArray<T, D> {}
@@ -135,134 +131,11 @@ unsafe impl<T: Element, D: Dimension> PyTypeInfo for PyArray<T, D> {
         unsafe { npyffi::PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type) }
     }
 
-    fn is_type_of_bound(ob: &Bound<'_, PyAny>) -> bool {
+    fn is_type_of(ob: &Bound<'_, PyAny>) -> bool {
         Self::extract::<IgnoreError>(ob).is_ok()
     }
 }
 
-pyobject_native_type_base!(PyArray<T, D>; T; D);
-
-impl<T, D> AsRef<PyAny> for PyArray<T, D> {
-    #[inline]
-    fn as_ref(&self) -> &PyAny {
-        &self.0
-    }
-}
-
-impl<T, D> Deref for PyArray<T, D> {
-    type Target = PyUntypedArray;
-
-    #[inline]
-    fn deref(&self) -> &Self::Target {
-        self.as_untyped()
-    }
-}
-
-unsafe impl<T, D> AsPyPointer for PyArray<T, D> {
-    #[inline]
-    fn as_ptr(&self) -> *mut ffi::PyObject {
-        self.0.as_ptr()
-    }
-}
-
-impl<T, D> IntoPy<Py<PyArray<T, D>>> for &'_ PyArray<T, D> {
-    #[inline]
-    fn into_py<'py>(self, py: Python<'py>) -> Py<PyArray<T, D>> {
-        unsafe { Py::from_borrowed_ptr(py, self.as_ptr()) }
-    }
-}
-
-#[cfg(feature = "gil-refs")]
-impl<T, D> From<&'_ PyArray<T, D>> for Py<PyArray<T, D>> {
-    #[inline]
-    fn from(other: &PyArray<T, D>) -> Self {
-        unsafe { Py::from_borrowed_ptr(other.py(), other.as_ptr()) }
-    }
-}
-
-impl<'a, T, D> From<&'a PyArray<T, D>> for &'a PyAny {
-    fn from(ob: &'a PyArray<T, D>) -> Self {
-        unsafe { &*(ob as *const PyArray<T, D> as *const PyAny) }
-    }
-}
-
-impl<T, D> IntoPy<PyObject> for PyArray<T, D> {
-    fn into_py<'py>(self, py: Python<'py>) -> PyObject {
-        unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
-    }
-}
-
-#[cfg(feature = "gil-refs")]
-impl<'py, T: Element, D: Dimension> FromPyObject<'py> for &'py PyArray<T, D> {
-    fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
-        PyArray::extract(ob).cloned().map(Bound::into_gil_ref)
-    }
-}
-
-impl<T, D> PyArray<T, D> {
-    /// Access an untyped representation of this array.
-    #[inline(always)]
-    pub fn as_untyped(&self) -> &PyUntypedArray {
-        unsafe { &*(self as *const Self as *const PyUntypedArray) }
-    }
-}
-
-#[cfg(feature = "gil-refs")]
-impl<T, D> PyArray<T, D> {
-    /// Turn `&PyArray<T,D>` into `Py<PyArray<T,D>>`,
-    /// i.e. a pointer into Python's heap which is independent of the GIL lifetime.
-    ///
-    /// This method can be used to avoid lifetime annotations of function arguments
-    /// or return values.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray1, PyArrayMethods};
-    /// use pyo3::{Py, Python};
-    ///
-    /// let array: Py<PyArray1<f64>> = Python::with_gil(|py| {
-    ///     PyArray1::zeros_bound(py, 5, false).unbind()
-    /// });
-    ///
-    /// Python::with_gil(|py| {
-    ///     assert_eq!(array.bind(py).readonly().as_slice().unwrap(), [0.0; 5]);
-    /// });
-    /// ```
-    #[deprecated(since = "0.21.0", note = "use Bound::unbind() instead")]
-    pub fn to_owned(&self) -> Py<Self> {
-        unsafe { Py::from_borrowed_ptr(self.py(), self.as_ptr()) }
-    }
-
-    /// Constructs a reference to a `PyArray` from a raw pointer to a Python object.
-    ///
-    /// # Safety
-    ///
-    /// This is a wrapper around [`pyo3::FromPyPointer::from_owned_ptr_or_opt`] and inherits its safety contract.
-    #[deprecated(since = "0.21.0", note = "use Bound::from_owned_ptr() instead")]
-    pub unsafe fn from_owned_ptr<'py>(py: Python<'py>, ptr: *mut ffi::PyObject) -> &'py Self {
-        #![allow(deprecated)]
-        py.from_owned_ptr(ptr)
-    }
-
-    /// Constructs a reference to a `PyArray` from a raw point to a Python object.
-    ///
-    /// # Safety
-    ///
-    /// This is a wrapper around [`pyo3::FromPyPointer::from_borrowed_ptr_or_opt`] and inherits its safety contract.
-    #[deprecated(since = "0.21.0", note = "use Bound::from_borrowed_ptr() instead")]
-    pub unsafe fn from_borrowed_ptr<'py>(py: Python<'py>, ptr: *mut ffi::PyObject) -> &'py Self {
-        #![allow(deprecated)]
-        py.from_borrowed_ptr(ptr)
-    }
-
-    /// Returns a pointer to the first element of the array.
-    #[inline(always)]
-    pub fn data(&self) -> *mut T {
-        unsafe { (*self.as_array_ptr()).data as *mut _ }
-    }
-}
-
 impl<T: Element, D: Dimension> PyArray<T, D> {
     fn extract<'a, 'py, E>(ob: &'a Bound<'py, PyAny>) -> Result<&'a Bound<'py, Self>, E>
     where
@@ -286,7 +159,7 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
 
         // Check if the element type matches `T`.
         let src_dtype = array.dtype();
-        let dst_dtype = T::get_dtype_bound(ob.py());
+        let dst_dtype = T::get_dtype(ob.py());
         if !src_dtype.is_equiv_to(&dst_dtype) {
             return Err(TypeError::new(src_dtype, dst_dtype).into());
         }
@@ -319,7 +192,7 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
     ///
     /// Python::with_gil(|py| {
     ///     let arr = unsafe {
-    ///         let arr = PyArray3::<i32>::new_bound(py, [4, 5, 6], false);
+    ///         let arr = PyArray3::<i32>::new(py, [4, 5, 6], false);
     ///
     ///         for i in 0..4 {
     ///             for j in 0..5 {
@@ -335,6 +208,20 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
     ///     assert_eq!(arr.shape(), &[4, 5, 6]);
     /// });
     /// ```
+    pub unsafe fn new<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> Bound<'py, Self>
+    where
+        ID: IntoDimension<Dim = D>,
+    {
+        let flags = c_int::from(is_fortran);
+        Self::new_uninit(py, dims, ptr::null_mut(), flags)
+    }
+
+    /// Deprecated name for [`PyArray::new`].
+    ///
+    /// # Safety
+    /// See [`PyArray::new`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::new`")]
+    #[inline]
     pub unsafe fn new_bound<'py, ID>(
         py: Python<'py>,
         dims: ID,
@@ -343,8 +230,7 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
     where
         ID: IntoDimension<Dim = D>,
     {
-        let flags = c_int::from(is_fortran);
-        Self::new_uninit(py, dims, ptr::null_mut(), flags)
+        Self::new(py, dims, is_fortran)
     }
 
     pub(crate) unsafe fn new_uninit<'py, ID>(
@@ -360,7 +246,7 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
         let ptr = PY_ARRAY_API.PyArray_NewFromDescr(
             py,
             PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type),
-            T::get_dtype_bound(py).into_dtype_ptr(),
+            T::get_dtype(py).into_dtype_ptr(),
             dims.ndim_cint(),
             dims.as_dims_ptr(),
             strides as *mut npy_intp, // strides
@@ -386,7 +272,7 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
         let ptr = PY_ARRAY_API.PyArray_NewFromDescr(
             py,
             PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type),
-            T::get_dtype_bound(py).into_dtype_ptr(),
+            T::get_dtype(py).into_dtype_ptr(),
             dims.ndim_cint(),
             dims.as_dims_ptr(),
             strides as *mut npy_intp,    // strides
@@ -447,11 +333,11 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
     ///
     ///         // SAFETY: The memory backing `array` will stay valid as long as this object is alive
     ///         // as we do not modify `array` in any way which would cause it to be reallocated.
-    ///         unsafe { PyArray1::borrow_from_array_bound(array, this.into_any()) }
+    ///         unsafe { PyArray1::borrow_from_array(array, this.into_any()) }
     ///     }
     /// }
     /// ```
-    pub unsafe fn borrow_from_array_bound<'py, S>(
+    pub unsafe fn borrow_from_array<'py, S>(
         array: &ArrayBase<S, D>,
         container: Bound<'py, PyAny>,
     ) -> Bound<'py, Self>
@@ -472,6 +358,22 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
         )
     }
 
+    /// Deprecated name for [`PyArray::borrow_from_array`].
+    ///
+    /// # Safety
+    /// See [`PyArray::borrow_from_array`]
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::borrow_from_array`")]
+    #[inline]
+    pub unsafe fn borrow_from_array_bound<'py, S>(
+        array: &ArrayBase<S, D>,
+        container: Bound<'py, PyAny>,
+    ) -> Bound<'py, Self>
+    where
+        S: Data<Elem = T>,
+    {
+        Self::borrow_from_array(array, container)
+    }
+
     /// Construct a new NumPy array filled with zeros.
     ///
     /// If `is_fortran` is true, then it has Fortran/column-major order,
@@ -489,7 +391,7 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray2::<usize>::zeros_bound(py, [2, 2], true);
+    ///     let pyarray = PyArray2::<usize>::zeros(py, [2, 2], true);
     ///
     ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), [0; 4]);
     /// });
@@ -497,7 +399,7 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
     ///
     /// [numpy-zeros]: https://numpy.org/doc/stable/reference/generated/numpy.zeros.html
     /// [PyArray_Zeros]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Zeros
-    pub fn zeros_bound<ID>(py: Python<'_>, dims: ID, is_fortran: bool) -> Bound<'_, Self>
+    pub fn zeros<ID>(py: Python<'_>, dims: ID, is_fortran: bool) -> Bound<'_, Self>
     where
         ID: IntoDimension<Dim = D>,
     {
@@ -507,13 +409,23 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
                 py,
                 dims.ndim_cint(),
                 dims.as_dims_ptr(),
-                T::get_dtype_bound(py).into_dtype_ptr(),
+                T::get_dtype(py).into_dtype_ptr(),
                 if is_fortran { -1 } else { 0 },
             );
             Bound::from_owned_ptr(py, ptr).downcast_into_unchecked()
         }
     }
 
+    /// Deprecated name for [`PyArray::zeros`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::zeros`")]
+    #[inline]
+    pub fn zeros_bound<ID>(py: Python<'_>, dims: ID, is_fortran: bool) -> Bound<'_, Self>
+    where
+        ID: IntoDimension<Dim = D>,
+    {
+        Self::zeros(py, dims, is_fortran)
+    }
+
     /// Constructs a NumPy from an [`ndarray::Array`]
     ///
     /// This method uses the internal [`Vec`] of the [`ndarray::Array`] as the base object of the NumPy array.
@@ -526,12 +438,12 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::from_owned_array_bound(py, array![[1, 2], [3, 4]]);
+    ///     let pyarray = PyArray::from_owned_array(py, array![[1, 2], [3, 4]]);
     ///
     ///     assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]);
     /// });
     /// ```
-    pub fn from_owned_array_bound(py: Python<'_>, mut arr: Array<T, D>) -> Bound<'_, Self> {
+    pub fn from_owned_array(py: Python<'_>, mut arr: Array<T, D>) -> Bound<'_, Self> {
         let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
         let data_ptr = arr.as_mut_ptr();
         unsafe {
@@ -544,477 +456,50 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
             )
         }
     }
+    /// Deprecated name for [`PyArray::from_owned_array`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::from_owned_array`")]
+    #[inline]
+    pub fn from_owned_array_bound(py: Python<'_>, arr: Array<T, D>) -> Bound<'_, Self> {
+        Self::from_owned_array(py, arr)
+    }
 
     /// Construct a NumPy array from a [`ndarray::ArrayBase`].
     ///
     /// This method allocates memory in Python's heap via the NumPy API,
-    /// and then copies all elements of the array there.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray, PyArrayMethods};
-    /// use ndarray::array;
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::from_array_bound(py, &array![[1, 2], [3, 4]]);
-    ///
-    ///     assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]);
-    /// });
-    /// ```
-    pub fn from_array_bound<'py, S>(py: Python<'py>, arr: &ArrayBase<S, D>) -> Bound<'py, Self>
-    where
-        S: Data<Elem = T>,
-    {
-        ToPyArray::to_pyarray_bound(arr, py)
-    }
-}
-
-#[cfg(feature = "gil-refs")]
-impl<T: Element, D: Dimension> PyArray<T, D> {
-    /// Same as [`shape`][PyUntypedArray::shape], but returns `D` instead of `&[usize]`.
-    #[inline(always)]
-    pub fn dims(&self) -> D {
-        D::from_dimension(&Dim(self.shape())).expect(DIMENSIONALITY_MISMATCH_ERR)
-    }
-
-    /// Deprecated form of [`PyArray<T, D>::new_bound`]
-    ///
-    /// # Safety
-    /// Same as [`PyArray<T, D>::new_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `PyArray::new_bound` in the future"
-    )]
-    pub unsafe fn new<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> &Self
-    where
-        ID: IntoDimension<Dim = D>,
-    {
-        Self::new_bound(py, dims, is_fortran).into_gil_ref()
-    }
-
-    /// Deprecated form of [`PyArray<T, D>::borrow_from_array_bound`]
-    ///
-    /// # Safety
-    /// Same as [`PyArray<T, D>::borrow_from_array_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `PyArray::borrow_from_array_bound` in the future"
-    )]
-    pub unsafe fn borrow_from_array<'py, S>(
-        array: &ArrayBase<S, D>,
-        container: &'py PyAny,
-    ) -> &'py Self
-    where
-        S: Data<Elem = T>,
-    {
-        Self::borrow_from_array_bound(array, (*container.as_borrowed()).clone()).into_gil_ref()
-    }
-
-    /// Deprecated form of [`PyArray<T, D>::zeros_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `PyArray::zeros_bound` in the future"
-    )]
-    pub fn zeros<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> &Self
-    where
-        ID: IntoDimension<Dim = D>,
-    {
-        Self::zeros_bound(py, dims, is_fortran).into_gil_ref()
-    }
-
-    /// Returns an immutable view of the internal data as a slice.
-    ///
-    /// # Safety
-    ///
-    /// Calling this method is undefined behaviour if the underlying array
-    /// is aliased mutably by other instances of `PyArray`
-    /// or concurrently modified by Python or other native code.
-    ///
-    /// Please consider the safe alternative [`PyReadonlyArray::as_slice`].
-    pub unsafe fn as_slice(&self) -> Result<&[T], NotContiguousError> {
-        if self.is_contiguous() {
-            Ok(slice::from_raw_parts(self.data(), self.len()))
-        } else {
-            Err(NotContiguousError)
-        }
-    }
-
-    /// Returns a mutable view of the internal data as a slice.
-    ///
-    /// # Safety
-    ///
-    /// Calling this method is undefined behaviour if the underlying array
-    /// is aliased immutably or mutably by other instances of [`PyArray`]
-    /// or concurrently modified by Python or other native code.
-    ///
-    /// Please consider the safe alternative [`PyReadwriteArray::as_slice_mut`].
-    pub unsafe fn as_slice_mut(&self) -> Result<&mut [T], NotContiguousError> {
-        if self.is_contiguous() {
-            Ok(slice::from_raw_parts_mut(self.data(), self.len()))
-        } else {
-            Err(NotContiguousError)
-        }
-    }
-
-    /// Deprecated form of [`PyArray<T, D>::from_owned_array_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by PyArray::from_owned_array_bound in the future"
-    )]
-    pub fn from_owned_array<'py>(py: Python<'py>, arr: Array<T, D>) -> &'py Self {
-        Self::from_owned_array_bound(py, arr).into_gil_ref()
-    }
-
-    /// Get a reference of the specified element if the given index is valid.
-    ///
-    /// # Safety
-    ///
-    /// Calling this method is undefined behaviour if the underlying array
-    /// is aliased mutably by other instances of `PyArray`
-    /// or concurrently modified by Python or other native code.
-    ///
-    /// Consider using safe alternatives like [`PyReadonlyArray::get`].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray, PyArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
-    ///
-    ///     assert_eq!(unsafe { *pyarray.get([1, 0, 3]).unwrap() }, 11);
-    /// });
-    /// ```
-    #[inline(always)]
-    pub unsafe fn get(&self, index: impl NpyIndex<Dim = D>) -> Option<&T> {
-        let ptr = get_raw(&self.as_borrowed(), index)?;
-        Some(&*ptr)
-    }
-
-    /// Same as [`get`][Self::get], but returns `Option<&mut T>`.
-    ///
-    /// # Safety
-    ///
-    /// Calling this method is undefined behaviour if the underlying array
-    /// is aliased immutably or mutably by other instances of [`PyArray`]
-    /// or concurrently modified by Python or other native code.
-    ///
-    /// Consider using safe alternatives like [`PyReadwriteArray::get_mut`].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray, PyArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
-    ///
-    ///     unsafe {
-    ///         *pyarray.get_mut([1, 0, 3]).unwrap() = 42;
-    ///     }
-    ///
-    ///     assert_eq!(unsafe { *pyarray.get([1, 0, 3]).unwrap() }, 42);
-    /// });
-    /// ```
-    #[inline(always)]
-    pub unsafe fn get_mut(&self, index: impl NpyIndex<Dim = D>) -> Option<&mut T> {
-        let ptr = get_raw(&self.as_borrowed(), index)?;
-        Some(&mut *ptr)
-    }
-
-    /// Get an immutable reference of the specified element,
-    /// without checking the given index.
-    ///
-    /// See [`NpyIndex`] for what types can be used as the index.
-    ///
-    /// # Safety
-    ///
-    /// Passing an invalid index is undefined behavior.
-    /// The element must also have been initialized and
-    /// all other references to it is must also be shared.
-    ///
-    /// See [`PyReadonlyArray::get`] for a safe alternative.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray, PyArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
-    ///
-    ///     assert_eq!(unsafe { *pyarray.uget([1, 0, 3]) }, 11);
-    /// });
-    /// ```
-    #[inline(always)]
-    pub unsafe fn uget<Idx>(&self, index: Idx) -> &T
-    where
-        Idx: NpyIndex<Dim = D>,
-    {
-        &*self.uget_raw(index)
-    }
-
-    /// Same as [`uget`](Self::uget), but returns `&mut T`.
-    ///
-    /// # Safety
-    ///
-    /// Passing an invalid index is undefined behavior.
-    /// The element must also have been initialized and
-    /// other references to it must not exist.
-    ///
-    /// See [`PyReadwriteArray::get_mut`] for a safe alternative.
-    #[inline(always)]
-    #[allow(clippy::mut_from_ref)]
-    pub unsafe fn uget_mut<Idx>(&self, index: Idx) -> &mut T
-    where
-        Idx: NpyIndex<Dim = D>,
-    {
-        &mut *self.uget_raw(index)
-    }
-
-    /// Same as [`uget`][Self::uget], but returns `*mut T`.
-    ///
-    /// # Safety
-    ///
-    /// Passing an invalid index is undefined behavior.
-    #[inline(always)]
-    pub unsafe fn uget_raw<Idx>(&self, index: Idx) -> *mut T
-    where
-        Idx: NpyIndex<Dim = D>,
-    {
-        self.as_borrowed().uget_raw(index)
-    }
-
-    /// Get a copy of the specified element in the array.
-    ///
-    /// See [`NpyIndex`] for what types can be used as the index.
-    ///
-    /// # Example
-    /// ```
-    /// use numpy::{PyArray, PyArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
-    ///
-    ///     assert_eq!(pyarray.get_owned([1, 0, 3]), Some(11));
-    /// });
-    /// ```
-    pub fn get_owned<Idx>(&self, index: Idx) -> Option<T>
-    where
-        Idx: NpyIndex<Dim = D>,
-    {
-        self.as_borrowed().get_owned(index)
-    }
-
-    /// Turn an array with fixed dimensionality into one with dynamic dimensionality.
-    pub fn to_dyn(&self) -> &PyArray<T, IxDyn> {
-        self.as_borrowed().to_dyn().clone().into_gil_ref()
-    }
-
-    /// Returns a copy of the internal data of the array as a [`Vec`].
-    ///
-    /// Fails if the internal array is not contiguous. See also [`as_slice`][Self::as_slice].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray2, PyArrayMethods};
-    /// use pyo3::{Python, types::PyAnyMethods};
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray= py
-    ///         .eval_bound("__import__('numpy').array([[0, 1], [2, 3]], dtype='int64')", None, None)
-    ///         .unwrap()
-    ///         .downcast_into::<PyArray2<i64>>()
-    ///         .unwrap();
-    ///
-    ///     assert_eq!(pyarray.to_vec().unwrap(), vec![0, 1, 2, 3]);
-    /// });
-    /// ```
-    pub fn to_vec(&self) -> Result<Vec<T>, NotContiguousError> {
-        self.as_borrowed().to_vec()
-    }
-
-    /// Deprecated form of [`PyArray<T, D>::from_array_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by PyArray::from_array_bound in the future"
-    )]
-    pub fn from_array<'py, S>(py: Python<'py>, arr: &ArrayBase<S, D>) -> &'py Self
-    where
-        S: Data<Elem = T>,
-    {
-        Self::from_array_bound(py, arr).into_gil_ref()
-    }
-
-    /// Get an immutable borrow of the NumPy array
-    pub fn try_readonly(&self) -> Result<PyReadonlyArray<'_, T, D>, BorrowError> {
-        PyReadonlyArray::try_new(self.as_borrowed().to_owned())
-    }
-
-    /// Get an immutable borrow of the NumPy array
-    ///
-    /// # Panics
-    ///
-    /// Panics if the allocation backing the array is currently mutably borrowed.
-    ///
-    /// For a non-panicking variant, use [`try_readonly`][Self::try_readonly].
-    pub fn readonly(&self) -> PyReadonlyArray<'_, T, D> {
-        self.try_readonly().unwrap()
-    }
-
-    /// Get a mutable borrow of the NumPy array
-    pub fn try_readwrite(&self) -> Result<PyReadwriteArray<'_, T, D>, BorrowError> {
-        PyReadwriteArray::try_new(self.as_borrowed().to_owned())
-    }
-
-    /// Get a mutable borrow of the NumPy array
-    ///
-    /// # Panics
-    ///
-    /// Panics if the allocation backing the array is currently borrowed or
-    /// if the array is [flagged as][flags] not writeable.
-    ///
-    /// For a non-panicking variant, use [`try_readwrite`][Self::try_readwrite].
-    ///
-    /// [flags]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.flags.html
-    pub fn readwrite(&self) -> PyReadwriteArray<'_, T, D> {
-        self.try_readwrite().unwrap()
-    }
-
-    /// Returns an [`ArrayView`] of the internal array.
-    ///
-    /// See also [`PyReadonlyArray::as_array`].
-    ///
-    /// # Safety
-    ///
-    /// Calling this method invalidates all exclusive references to the internal data, e.g. `&mut [T]` or `ArrayViewMut`.
-    pub unsafe fn as_array(&self) -> ArrayView<'_, T, D> {
-        as_view(&self.as_borrowed(), |shape, ptr| {
-            ArrayView::from_shape_ptr(shape, ptr)
-        })
-    }
-
-    /// Returns an [`ArrayViewMut`] of the internal array.
-    ///
-    /// See also [`PyReadwriteArray::as_array_mut`].
-    ///
-    /// # Safety
-    ///
-    /// Calling this method invalidates all other references to the internal data, e.g. `ArrayView` or `ArrayViewMut`.
-    pub unsafe fn as_array_mut(&self) -> ArrayViewMut<'_, T, D> {
-        as_view(&self.as_borrowed(), |shape, ptr| {
-            ArrayViewMut::from_shape_ptr(shape, ptr)
-        })
-    }
-
-    /// Returns the internal array as [`RawArrayView`] enabling element access via raw pointers
-    pub fn as_raw_array(&self) -> RawArrayView<T, D> {
-        self.as_borrowed().as_raw_array()
-    }
-
-    /// Returns the internal array as [`RawArrayViewMut`] enabling element access via raw pointers
-    pub fn as_raw_array_mut(&self) -> RawArrayViewMut<T, D> {
-        self.as_borrowed().as_raw_array_mut()
-    }
-
-    /// Get a copy of the array as an [`ndarray::Array`].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray, PyArrayMethods};
-    /// use ndarray::array;
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 4, 1).reshape([2, 2]).unwrap();
-    ///
-    ///     assert_eq!(
-    ///         pyarray.to_owned_array(),
-    ///         array![[0, 1], [2, 3]]
-    ///     )
-    /// });
-    /// ```
-    pub fn to_owned_array(&self) -> Array<T, D> {
-        self.as_borrowed().to_owned_array()
-    }
-}
-
-#[cfg(feature = "nalgebra")]
-#[cfg(feature = "gil-refs")]
-impl<N, D> PyArray<N, D>
-where
-    N: nalgebra::Scalar + Element,
-    D: Dimension,
-{
-    /// Try to convert this array into a [`nalgebra::MatrixView`] using the given shape and strides.
+    /// and then copies all elements of the array there.
     ///
-    /// See [`PyReadonlyArray::try_as_matrix`] for a discussion of the memory layout requirements.
+    /// # Example
     ///
-    /// # Safety
+    /// ```
+    /// use numpy::{PyArray, PyArrayMethods};
+    /// use ndarray::array;
+    /// use pyo3::Python;
     ///
-    /// Calling this method invalidates all exclusive references to the internal data, e.g. `ArrayViewMut` or `MatrixSliceMut`.
-    #[doc(alias = "nalgebra")]
-    pub unsafe fn try_as_matrix<R, C, RStride, CStride>(
-        &self,
-    ) -> Option<nalgebra::MatrixView<'_, N, R, C, RStride, CStride>>
+    /// Python::with_gil(|py| {
+    ///     let pyarray = PyArray::from_array(py, &array![[1, 2], [3, 4]]);
+    ///
+    ///     assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]);
+    /// });
+    /// ```
+    pub fn from_array<'py, S>(py: Python<'py>, arr: &ArrayBase<S, D>) -> Bound<'py, Self>
     where
-        R: nalgebra::Dim,
-        C: nalgebra::Dim,
-        RStride: nalgebra::Dim,
-        CStride: nalgebra::Dim,
+        S: Data<Elem = T>,
     {
-        let (shape, strides) = try_as_matrix_shape_strides(&self.as_borrowed())?;
-
-        let storage = nalgebra::ViewStorage::from_raw_parts(self.data(), shape, strides);
-
-        Some(nalgebra::Matrix::from_data(storage))
+        ToPyArray::to_pyarray(arr, py)
     }
 
-    /// Try to convert this array into a [`nalgebra::MatrixViewMut`] using the given shape and strides.
-    ///
-    /// See [`PyReadonlyArray::try_as_matrix`] for a discussion of the memory layout requirements.
-    ///
-    /// # Safety
-    ///
-    /// Calling this method invalidates all other references to the internal data, e.g. `ArrayView`, `MatrixSlice`, `ArrayViewMut` or `MatrixSliceMut`.
-    #[doc(alias = "nalgebra")]
-    pub unsafe fn try_as_matrix_mut<R, C, RStride, CStride>(
-        &self,
-    ) -> Option<nalgebra::MatrixViewMut<'_, N, R, C, RStride, CStride>>
+    /// Deprecated name for [`PyArray::from_array`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::from_array`")]
+    #[inline]
+    pub fn from_array_bound<'py, S>(py: Python<'py>, arr: &ArrayBase<S, D>) -> Bound<'py, Self>
     where
-        R: nalgebra::Dim,
-        C: nalgebra::Dim,
-        RStride: nalgebra::Dim,
-        CStride: nalgebra::Dim,
+        S: Data<Elem = T>,
     {
-        let (shape, strides) = try_as_matrix_shape_strides(&self.as_borrowed())?;
-
-        let storage = nalgebra::ViewStorageMut::from_raw_parts(self.data(), shape, strides);
-
-        Some(nalgebra::Matrix::from_data(storage))
+        Self::from_array(py, arr)
     }
 }
 
 impl<D: Dimension> PyArray<PyObject, D> {
-    /// Deprecated form of [`PyArray<T, D>::from_owned_object_array_bound`]
-    #[cfg(feature = "gil-refs")]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by PyArray::from_owned_object_array_bound in the future"
-    )]
-    pub fn from_owned_object_array<'py, T>(py: Python<'py>, arr: Array<Py<T>, D>) -> &'py Self {
-        Self::from_owned_object_array_bound(py, arr).into_gil_ref()
-    }
-
     /// Construct a NumPy array containing objects stored in a [`ndarray::Array`]
     ///
     /// This method uses the internal [`Vec`] of the [`ndarray::Array`] as the base object of the NumPy array.
@@ -1045,15 +530,12 @@ impl<D: Dimension> PyArray<PyObject, D> {
     ///         }).unwrap(),
     ///     ];
     ///
-    ///     let pyarray = PyArray::from_owned_object_array_bound(py, array);
+    ///     let pyarray = PyArray::from_owned_object_array(py, array);
     ///
     ///     assert!(pyarray.readonly().as_array().get(0).unwrap().bind(py).is_instance_of::<CustomElement>());
     /// });
     /// ```
-    pub fn from_owned_object_array_bound<T>(
-        py: Python<'_>,
-        mut arr: Array<Py<T>, D>,
-    ) -> Bound<'_, Self> {
+    pub fn from_owned_object_array<T>(py: Python<'_>, mut arr: Array<Py<T>, D>) -> Bound<'_, Self> {
         let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
         let data_ptr = arr.as_mut_ptr() as *const PyObject;
         unsafe {
@@ -1066,15 +548,18 @@ impl<D: Dimension> PyArray<PyObject, D> {
             )
         }
     }
-}
 
-#[cfg(feature = "gil-refs")]
-impl<T: Copy + Element> PyArray<T, Ix0> {
-    /// Get the single element of a zero-dimensional array.
-    ///
-    /// See [`inner`][crate::inner] for an example.
-    pub fn item(&self) -> T {
-        self.as_borrowed().item()
+    /// Deprecated name for [`PyArray::from_owned_object_array`].
+    #[deprecated(
+        since = "0.23.0",
+        note = "renamed to `PyArray::from_owned_object_array`"
+    )]
+    #[inline]
+    pub fn from_owned_object_array_bound<T>(
+        py: Python<'_>,
+        arr: Array<Py<T>, D>,
+    ) -> Bound<'_, Self> {
+        Self::from_owned_object_array(py, arr)
     }
 }
 
@@ -1089,19 +574,26 @@ impl<T: Element> PyArray<T, Ix1> {
     ///
     /// Python::with_gil(|py| {
     ///     let slice = &[1, 2, 3, 4, 5];
-    ///     let pyarray = PyArray::from_slice_bound(py, slice);
+    ///     let pyarray = PyArray::from_slice(py, slice);
     ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[1, 2, 3, 4, 5]);
     /// });
     /// ```
-    pub fn from_slice_bound<'py>(py: Python<'py>, slice: &[T]) -> Bound<'py, Self> {
+    pub fn from_slice<'py>(py: Python<'py>, slice: &[T]) -> Bound<'py, Self> {
         unsafe {
-            let array = PyArray::new_bound(py, [slice.len()], false);
+            let array = PyArray::new(py, [slice.len()], false);
             let mut data_ptr = array.data();
             clone_elements(py, slice, &mut data_ptr);
             array
         }
     }
 
+    /// Deprecated name for [`PyArray::from_slice`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::from_slice`")]
+    #[inline]
+    pub fn from_slice_bound<'py>(py: Python<'py>, slice: &[T]) -> Bound<'py, Self> {
+        Self::from_slice(py, slice)
+    }
+
     /// Construct a one-dimensional array from a [`Vec<T>`][Vec].
     ///
     /// # Example
@@ -1112,13 +604,20 @@ impl<T: Element> PyArray<T, Ix1> {
     ///
     /// Python::with_gil(|py| {
     ///     let vec = vec![1, 2, 3, 4, 5];
-    ///     let pyarray = PyArray::from_vec_bound(py, vec);
+    ///     let pyarray = PyArray::from_vec(py, vec);
     ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[1, 2, 3, 4, 5]);
     /// });
     /// ```
     #[inline(always)]
+    pub fn from_vec<'py>(py: Python<'py>, vec: Vec<T>) -> Bound<'py, Self> {
+        vec.into_pyarray(py)
+    }
+
+    /// Deprecated name for [`PyArray::from_vec`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::from_vec`")]
+    #[inline]
     pub fn from_vec_bound<'py>(py: Python<'py>, vec: Vec<T>) -> Bound<'py, Self> {
-        vec.into_pyarray_bound(py)
+        Self::from_vec(py, vec)
     }
 
     /// Construct a one-dimensional array from an [`Iterator`].
@@ -1133,64 +632,30 @@ impl<T: Element> PyArray<T, Ix1> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::from_iter_bound(py, "abcde".chars().map(u32::from));
+    ///     let pyarray = PyArray::from_iter(py, "abcde".chars().map(u32::from));
     ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[97, 98, 99, 100, 101]);
     /// });
     /// ```
-    pub fn from_iter_bound<I>(py: Python<'_>, iter: I) -> Bound<'_, Self>
+    pub fn from_iter<I>(py: Python<'_>, iter: I) -> Bound<'_, Self>
     where
         I: IntoIterator<Item = T>,
     {
         let data = iter.into_iter().collect::<Vec<_>>();
-        data.into_pyarray_bound(py)
-    }
-}
-
-#[cfg(feature = "gil-refs")]
-impl<T: Element> PyArray<T, Ix1> {
-    /// Deprecated form of [`PyArray<T, Ix1>::from_slice_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `PyArray::from_slice_bound` in the future"
-    )]
-    pub fn from_slice<'py>(py: Python<'py>, slice: &[T]) -> &'py Self {
-        Self::from_slice_bound(py, slice).into_gil_ref()
-    }
-
-    /// Deprecated form of [`PyArray<T, Ix1>::from_vec_bound`]
-    #[inline(always)]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `PyArray::from_vec_bound` in the future"
-    )]
-    pub fn from_vec<'py>(py: Python<'py>, vec: Vec<T>) -> &'py Self {
-        Self::from_vec_bound(py, vec).into_gil_ref()
+        data.into_pyarray(py)
     }
 
-    /// Deprecated form of [`PyArray<T, Ix1>::from_iter_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by PyArray::from_iter_bound in the future"
-    )]
-    pub fn from_iter<'py, I>(py: Python<'py>, iter: I) -> &'py Self
+    /// Deprecated name for [`PyArray::from_iter`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::from_iter`")]
+    #[inline]
+    pub fn from_iter_bound<I>(py: Python<'_>, iter: I) -> Bound<'_, Self>
     where
         I: IntoIterator<Item = T>,
     {
-        Self::from_iter_bound(py, iter).into_gil_ref()
+        Self::from_iter(py, iter)
     }
 }
 
 impl<T: Element> PyArray<T, Ix2> {
-    /// Deprecated form of [`PyArray<T, Ix2>::from_vec2_bound`]
-    #[cfg(feature = "gil-refs")]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `PyArray::from_vec2_bound` in the future"
-    )]
-    pub fn from_vec2<'py>(py: Python<'py>, v: &[Vec<T>]) -> Result<&'py Self, FromVecError> {
-        Self::from_vec2_bound(py, v).map(Bound::into_gil_ref)
-    }
-
     /// Construct a two-dimension array from a [`Vec<Vec<T>>`][Vec].
     ///
     /// This function checks all dimensions of the inner vectors and returns
@@ -1205,22 +670,19 @@ impl<T: Element> PyArray<T, Ix2> {
     ///
     /// Python::with_gil(|py| {
     ///     let vec2 = vec![vec![11, 12], vec![21, 22]];
-    ///     let pyarray = PyArray::from_vec2_bound(py, &vec2).unwrap();
+    ///     let pyarray = PyArray::from_vec2(py, &vec2).unwrap();
     ///     assert_eq!(pyarray.readonly().as_array(), array![[11, 12], [21, 22]]);
     ///
     ///     let ragged_vec2 = vec![vec![11, 12], vec![21]];
-    ///     assert!(PyArray::from_vec2_bound(py, &ragged_vec2).is_err());
+    ///     assert!(PyArray::from_vec2(py, &ragged_vec2).is_err());
     /// });
     /// ```
-    pub fn from_vec2_bound<'py>(
-        py: Python<'py>,
-        v: &[Vec<T>],
-    ) -> Result<Bound<'py, Self>, FromVecError> {
+    pub fn from_vec2<'py>(py: Python<'py>, v: &[Vec<T>]) -> Result<Bound<'py, Self>, FromVecError> {
         let len2 = v.first().map_or(0, |v| v.len());
         let dims = [v.len(), len2];
         // SAFETY: The result of `Self::new` is always safe to drop.
         unsafe {
-            let array = Self::new_bound(py, dims, false);
+            let array = Self::new(py, dims, false);
             let mut data_ptr = array.data();
             for v in v {
                 if v.len() != len2 {
@@ -1232,19 +694,19 @@ impl<T: Element> PyArray<T, Ix2> {
             Ok(array)
         }
     }
-}
 
-impl<T: Element> PyArray<T, Ix3> {
-    /// Deprecated form of [`PyArray<T, Ix3>::from_vec3_bound`]
-    #[cfg(feature = "gil-refs")]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `PyArray::from_vec3_bound` in the future"
-    )]
-    pub fn from_vec3<'py>(py: Python<'py>, v: &[Vec<Vec<T>>]) -> Result<&'py Self, FromVecError> {
-        Self::from_vec3_bound(py, v).map(Bound::into_gil_ref)
+    /// Deprecated name for [`PyArray::from_vec2`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::from_vec2`")]
+    #[inline]
+    pub fn from_vec2_bound<'py>(
+        py: Python<'py>,
+        v: &[Vec<T>],
+    ) -> Result<Bound<'py, Self>, FromVecError> {
+        Self::from_vec2(py, v)
     }
+}
 
+impl<T: Element> PyArray<T, Ix3> {
     /// Construct a three-dimensional array from a [`Vec<Vec<Vec<T>>>`][Vec].
     ///
     /// This function checks all dimensions of the inner vectors and returns
@@ -1262,7 +724,7 @@ impl<T: Element> PyArray<T, Ix3> {
     ///         vec![vec![111, 112], vec![121, 122]],
     ///         vec![vec![211, 212], vec![221, 222]],
     ///     ];
-    ///     let pyarray = PyArray::from_vec3_bound(py, &vec3).unwrap();
+    ///     let pyarray = PyArray::from_vec3(py, &vec3).unwrap();
     ///     assert_eq!(
     ///         pyarray.readonly().as_array(),
     ///         array![[[111, 112], [121, 122]], [[211, 212], [221, 222]]]
@@ -1272,10 +734,10 @@ impl<T: Element> PyArray<T, Ix3> {
     ///         vec![vec![111, 112], vec![121, 122]],
     ///         vec![vec![211], vec![221, 222]],
     ///     ];
-    ///     assert!(PyArray::from_vec3_bound(py, &ragged_vec3).is_err());
+    ///     assert!(PyArray::from_vec3(py, &ragged_vec3).is_err());
     /// });
     /// ```
-    pub fn from_vec3_bound<'py>(
+    pub fn from_vec3<'py>(
         py: Python<'py>,
         v: &[Vec<Vec<T>>],
     ) -> Result<Bound<'py, Self>, FromVecError> {
@@ -1284,7 +746,7 @@ impl<T: Element> PyArray<T, Ix3> {
         let dims = [v.len(), len2, len3];
         // SAFETY: The result of `Self::new` is always safe to drop.
         unsafe {
-            let array = Self::new_bound(py, dims, false);
+            let array = Self::new(py, dims, false);
             let mut data_ptr = array.data();
             for v in v {
                 if v.len() != len2 {
@@ -1302,190 +764,19 @@ impl<T: Element> PyArray<T, Ix3> {
             Ok(array)
         }
     }
-}
-
-#[cfg(feature = "gil-refs")]
-impl<T: Element, D> PyArray<T, D> {
-    /// Copies `self` into `other`, performing a data type conversion if necessary.
-    ///
-    /// See also [`PyArray_CopyInto`][PyArray_CopyInto].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray, PyArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray_f = PyArray::arange_bound(py, 2.0, 5.0, 1.0);
-    ///     let pyarray_i = unsafe { PyArray::<i64, _>::new_bound(py, [3], false) };
-    ///
-    ///     assert!(pyarray_f.copy_to(&pyarray_i).is_ok());
-    ///
-    ///     assert_eq!(pyarray_i.readonly().as_slice().unwrap(), &[2, 3, 4]);
-    /// });
-    /// ```
-    ///
-    /// [PyArray_CopyInto]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_CopyInto
-    pub fn copy_to<U: Element>(&self, other: &PyArray<U, D>) -> PyResult<()> {
-        self.as_borrowed().copy_to(&other.as_borrowed())
-    }
-
-    /// Cast the `PyArray<T>` to `PyArray<U>`, by allocating a new array.
-    ///
-    /// See also [`PyArray_CastToType`][PyArray_CastToType].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray, PyArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray_f = PyArray::arange_bound(py, 2.0, 5.0, 1.0);
-    ///
-    ///     let pyarray_i = pyarray_f.cast::<i32>(false).unwrap();
-    ///
-    ///     assert_eq!(pyarray_i.readonly().as_slice().unwrap(), &[2, 3, 4]);
-    /// });
-    /// ```
-    ///
-    /// [PyArray_CastToType]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_CastToType
-    pub fn cast<'py, U: Element>(&'py self, is_fortran: bool) -> PyResult<&'py PyArray<U, D>> {
-        self.as_borrowed().cast(is_fortran).map(Bound::into_gil_ref)
-    }
-
-    /// A view of `self` with a different order of axes determined by `axes`.
-    ///
-    /// If `axes` is `None`, the order of axes is reversed which corresponds to the standard matrix transpose.
-    ///
-    /// See also [`numpy.transpose`][numpy-transpose] and [`PyArray_Transpose`][PyArray_Transpose].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::prelude::*;
-    /// use numpy::PyArray;
-    /// use pyo3::Python;
-    /// use ndarray::array;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let array = array![[0, 1, 2], [3, 4, 5]].into_pyarray(py);
-    ///
-    ///     let array = array.permute(Some([1, 0])).unwrap();
-    ///
-    ///     assert_eq!(array.readonly().as_array(), array![[0, 3], [1, 4], [2, 5]]);
-    /// });
-    /// ```
-    ///
-    /// [numpy-transpose]: https://numpy.org/doc/stable/reference/generated/numpy.transpose.html
-    /// [PyArray_Transpose]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Transpose
-    pub fn permute<'py, ID: IntoDimension>(
-        &'py self,
-        axes: Option<ID>,
-    ) -> PyResult<&'py PyArray<T, D>> {
-        self.as_borrowed().permute(axes).map(Bound::into_gil_ref)
-    }
-
-    /// Special case of [`permute`][Self::permute] which reverses the order the axes.
-    pub fn transpose<'py>(&'py self) -> PyResult<&'py PyArray<T, D>> {
-        self.as_borrowed().transpose().map(Bound::into_gil_ref)
-    }
-
-    /// Construct a new array which has same values as self,
-    /// but has different dimensions specified by `shape`
-    /// and a possibly different memory order specified by `order`.
-    ///
-    /// See also [`numpy.reshape`][numpy-reshape] and [`PyArray_Newshape`][PyArray_Newshape].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::prelude::*;
-    /// use numpy::{npyffi::NPY_ORDER, PyArray};
-    /// use pyo3::Python;
-    /// use ndarray::array;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let array =
-    ///         PyArray::from_iter_bound(py, 0..9).reshape_with_order([3, 3], NPY_ORDER::NPY_FORTRANORDER).unwrap();
-    ///
-    ///     assert_eq!(array.readonly().as_array(), array![[0, 3, 6], [1, 4, 7], [2, 5, 8]]);
-    ///     assert!(array.is_fortran_contiguous());
-    ///
-    ///     assert!(array.reshape([5]).is_err());
-    /// });
-    /// ```
-    ///
-    /// [numpy-reshape]: https://numpy.org/doc/stable/reference/generated/numpy.reshape.html
-    /// [PyArray_Newshape]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Newshape
-    pub fn reshape_with_order<'py, ID: IntoDimension>(
-        &'py self,
-        shape: ID,
-        order: NPY_ORDER,
-    ) -> PyResult<&'py PyArray<T, ID::Dim>> {
-        self.as_borrowed()
-            .reshape_with_order(shape, order)
-            .map(Bound::into_gil_ref)
-    }
-
-    /// Special case of [`reshape_with_order`][Self::reshape_with_order] which keeps the memory order the same.
-    #[inline(always)]
-    pub fn reshape<'py, ID: IntoDimension>(
-        &'py self,
-        shape: ID,
-    ) -> PyResult<&'py PyArray<T, ID::Dim>> {
-        self.as_borrowed().reshape(shape).map(Bound::into_gil_ref)
-    }
 
-    /// Extends or truncates the dimensions of an array.
-    ///
-    /// This method works only on [contiguous][PyUntypedArray::is_contiguous] arrays.
-    /// Missing elements will be initialized as if calling [`zeros`][Self::zeros].
-    ///
-    /// See also [`ndarray.resize`][ndarray-resize] and [`PyArray_Resize`][PyArray_Resize].
-    ///
-    /// # Safety
-    ///
-    /// There should be no outstanding references (shared or exclusive) into the array
-    /// as this method might re-allocate it and thereby invalidate all pointers into it.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::prelude::*;
-    /// use numpy::PyArray;
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::<f64, _>::zeros_bound(py, (10, 10), false);
-    ///     assert_eq!(pyarray.shape(), [10, 10]);
-    ///
-    ///     unsafe {
-    ///         pyarray.resize((100, 100)).unwrap();
-    ///     }
-    ///     assert_eq!(pyarray.shape(), [100, 100]);
-    /// });
-    /// ```
-    ///
-    /// [ndarray-resize]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.resize.html
-    /// [PyArray_Resize]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Resize
-    pub unsafe fn resize<ID: IntoDimension>(&self, newshape: ID) -> PyResult<()> {
-        self.as_borrowed().resize(newshape)
+    /// Deprecated name for [`PyArray::from_vec3`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::from_vec3`")]
+    #[inline]
+    pub fn from_vec3_bound<'py>(
+        py: Python<'py>,
+        v: &[Vec<Vec<T>>],
+    ) -> Result<Bound<'py, Self>, FromVecError> {
+        Self::from_vec3(py, v)
     }
 }
 
 impl<T: Element + AsPrimitive<f64>> PyArray<T, Ix1> {
-    /// Deprecated form of [`PyArray<T, Ix1>::arange_bound`]
-    #[cfg(feature = "gil-refs")]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by PyArray::arange_bound in the future"
-    )]
-    pub fn arange<'py>(py: Python<'py>, start: T, stop: T, step: T) -> &Self {
-        Self::arange_bound(py, start, stop, step).into_gil_ref()
-    }
-
     /// Return evenly spaced values within a given interval.
     ///
     /// See [numpy.arange][numpy.arange] for the Python API and [PyArray_Arange][PyArray_Arange] for the C API.
@@ -1497,28 +788,35 @@ impl<T: Element + AsPrimitive<f64>> PyArray<T, Ix1> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 2.0, 4.0, 0.5);
+    ///     let pyarray = PyArray::arange(py, 2.0, 4.0, 0.5);
     ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[2.0, 2.5, 3.0, 3.5]);
     ///
-    ///     let pyarray = PyArray::arange_bound(py, -2, 4, 3);
+    ///     let pyarray = PyArray::arange(py, -2, 4, 3);
     ///     assert_eq!(pyarray.readonly().as_slice().unwrap(), &[-2, 1]);
     /// });
     /// ```
     ///
     /// [numpy.arange]: https://numpy.org/doc/stable/reference/generated/numpy.arange.html
     /// [PyArray_Arange]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_Arange
-    pub fn arange_bound<'py>(py: Python<'py>, start: T, stop: T, step: T) -> Bound<'py, Self> {
+    pub fn arange<'py>(py: Python<'py>, start: T, stop: T, step: T) -> Bound<'py, Self> {
         unsafe {
             let ptr = PY_ARRAY_API.PyArray_Arange(
                 py,
                 start.as_(),
                 stop.as_(),
                 step.as_(),
-                T::get_dtype_bound(py).num(),
+                T::get_dtype(py).num(),
             );
             Bound::from_owned_ptr(py, ptr).downcast_into_unchecked()
         }
     }
+
+    /// Deprecated name for [`PyArray::arange`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArray::arange`")]
+    #[inline]
+    pub fn arange_bound<'py>(py: Python<'py>, start: T, stop: T, step: T) -> Bound<'py, Self> {
+        Self::arange(py, start, stop, step)
+    }
 }
 
 unsafe fn clone_elements<T: Element>(py: Python<'_>, elems: &[T], data_ptr: &mut *mut T) {
@@ -1610,7 +908,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
+    ///     let pyarray = PyArray::arange(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
     ///
     ///     assert_eq!(unsafe { *pyarray.get([1, 0, 3]).unwrap() }, 11);
     /// });
@@ -1637,7 +935,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
+    ///     let pyarray = PyArray::arange(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
     ///
     ///     unsafe {
     ///         *pyarray.get_mut([1, 0, 3]).unwrap() = 42;
@@ -1671,7 +969,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
+    ///     let pyarray = PyArray::arange(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
     ///
     ///     assert_eq!(unsafe { *pyarray.uget([1, 0, 3]) }, 11);
     /// });
@@ -1732,7 +1030,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
+    ///     let pyarray = PyArray::arange(py, 0, 16, 1).reshape([2, 2, 4]).unwrap();
     ///
     ///     assert_eq!(pyarray.get_owned([1, 0, 3]), Some(11));
     /// });
@@ -1757,17 +1055,18 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     ///
     /// ```
     /// use numpy::{PyArray2, PyArrayMethods};
-    /// use pyo3::{Python, types::PyAnyMethods};
+    /// use pyo3::{Python, types::PyAnyMethods, ffi::c_str};
     ///
+    /// # fn main() -> pyo3::PyResult<()> {
     /// Python::with_gil(|py| {
     ///     let pyarray= py
-    ///         .eval_bound("__import__('numpy').array([[0, 1], [2, 3]], dtype='int64')", None, None)
-    ///         .unwrap()
-    ///         .downcast_into::<PyArray2<i64>>()
-    ///         .unwrap();
+    ///         .eval(c_str!("__import__('numpy').array([[0, 1], [2, 3]], dtype='int64')"), None, None)?
+    ///         .downcast_into::<PyArray2<i64>>()?;
     ///
-    ///     assert_eq!(pyarray.to_vec().unwrap(), vec![0, 1, 2, 3]);
-    /// });
+    ///     assert_eq!(pyarray.to_vec()?, vec![0, 1, 2, 3]);
+    /// #   Ok(())
+    /// })
+    /// # }
     /// ```
     fn to_vec(&self) -> Result<Vec<T>, NotContiguousError>
     where
@@ -1865,7 +1164,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 4, 1).reshape([2, 2]).unwrap();
+    ///     let pyarray = PyArray::arange(py, 0, 4, 1).reshape([2, 2]).unwrap();
     ///
     ///     assert_eq!(
     ///         pyarray.to_owned_array(),
@@ -1889,8 +1188,8 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray_f = PyArray::arange_bound(py, 2.0, 5.0, 1.0);
-    ///     let pyarray_i = unsafe { PyArray::<i64, _>::new_bound(py, [3], false) };
+    ///     let pyarray_f = PyArray::arange(py, 2.0, 5.0, 1.0);
+    ///     let pyarray_i = unsafe { PyArray::<i64, _>::new(py, [3], false) };
     ///
     ///     assert!(pyarray_f.copy_to(&pyarray_i).is_ok());
     ///
@@ -1914,7 +1213,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray_f = PyArray::arange_bound(py, 2.0, 5.0, 1.0);
+    ///     let pyarray_f = PyArray::arange(py, 2.0, 5.0, 1.0);
     ///
     ///     let pyarray_i = pyarray_f.cast::<i32>(false).unwrap();
     ///
@@ -1942,7 +1241,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use ndarray::array;
     ///
     /// Python::with_gil(|py| {
-    ///     let array = array![[0, 1, 2], [3, 4, 5]].into_pyarray_bound(py);
+    ///     let array = array![[0, 1, 2], [3, 4, 5]].into_pyarray(py);
     ///
     ///     let array = array.permute(Some([1, 0])).unwrap();
     ///
@@ -1980,7 +1279,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     ///
     /// Python::with_gil(|py| {
     ///     let array =
-    ///         PyArray::from_iter_bound(py, 0..9).reshape_with_order([3, 3], NPY_ORDER::NPY_FORTRANORDER).unwrap();
+    ///         PyArray::from_iter(py, 0..9).reshape_with_order([3, 3], NPY_ORDER::NPY_FORTRANORDER).unwrap();
     ///
     ///     assert_eq!(array.readonly().as_array(), array![[0, 3, 6], [1, 4, 7], [2, 5, 8]]);
     ///     assert!(array.is_fortran_contiguous());
@@ -2011,7 +1310,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// Extends or truncates the dimensions of an array.
     ///
     /// This method works only on [contiguous][PyUntypedArrayMethods::is_contiguous] arrays.
-    /// Missing elements will be initialized as if calling [`zeros`][PyArray::zeros_bound].
+    /// Missing elements will be initialized as if calling [`zeros`][PyArray::zeros].
     ///
     /// See also [`ndarray.resize`][ndarray-resize] and [`PyArray_Resize`][PyArray_Resize].
     ///
@@ -2028,7 +1327,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::<f64, _>::zeros_bound(py, (10, 10), false);
+    ///     let pyarray = PyArray::<f64, _>::zeros(py, (10, 10), false);
     ///     assert_eq!(pyarray.shape(), [10, 10]);
     ///
     ///     unsafe {
@@ -2358,7 +1657,7 @@ impl<'py, T, D> PyArrayMethods<'py, T, D> for Bound<'py, PyArray<T, D>> {
             PY_ARRAY_API.PyArray_CastToType(
                 self.py(),
                 self.as_array_ptr(),
-                U::get_dtype_bound(self.py()).into_dtype_ptr(),
+                U::get_dtype(self.py()).into_dtype_ptr(),
                 if is_fortran { -1 } else { 0 },
             )
         };
@@ -2476,7 +1775,7 @@ mod tests {
     #[test]
     fn test_dyn_to_owned_array() {
         Python::with_gil(|py| {
-            let array = PyArray::from_vec2_bound(py, &[vec![1, 2], vec![3, 4]])
+            let array = PyArray::from_vec2(py, &[vec![1, 2], vec![3, 4]])
                 .unwrap()
                 .to_dyn()
                 .to_owned_array();
@@ -2489,7 +1788,7 @@ mod tests {
     fn test_hasobject_flag() {
         Python::with_gil(|py| {
             let array: Bound<'_, PyArray<PyObject, _>> =
-                PyArray1::from_slice_bound(py, &[PyList::empty_bound(py).into()]);
+                PyArray1::from_slice(py, &[PyList::empty(py).into()]);
 
             py_run!(py, array, "assert array.dtype.hasobject");
         });
diff --git a/src/array_like.rs b/src/array_like.rs
index 7f7749477..1c7a0b36b 100644
--- a/src/array_like.rs
+++ b/src/array_like.rs
@@ -154,7 +154,7 @@ where
                 let array = Array1::from(vec)
                     .into_dimensionality()
                     .expect("D being compatible to Ix1")
-                    .into_pyarray_bound(py)
+                    .into_pyarray(py)
                     .readonly();
                 return Ok(Self(array, PhantomData));
             }
@@ -169,8 +169,8 @@ where
             .bind(py);
 
         let kwargs = if C::VAL {
-            let kwargs = PyDict::new_bound(py);
-            kwargs.set_item(intern!(py, "dtype"), T::get_dtype_bound(py))?;
+            let kwargs = PyDict::new(py);
+            kwargs.set_item(intern!(py, "dtype"), T::get_dtype(py))?;
             Some(kwargs)
         } else {
             None
diff --git a/src/borrow/mod.rs b/src/borrow/mod.rs
index bcfb4028c..de709c193 100644
--- a/src/borrow/mod.rs
+++ b/src/borrow/mod.rs
@@ -41,9 +41,9 @@
 //! }
 //!
 //! Python::with_gil(|py| {
-//!     let x = PyArray1::<f64>::zeros_bound(py, 42, false);
-//!     let y = PyArray1::<f64>::zeros_bound(py, 42, false);
-//!     let z = PyArray1::<f64>::zeros_bound(py, 42, false);
+//!     let x = PyArray1::<f64>::zeros(py, 42, false);
+//!     let y = PyArray1::<f64>::zeros(py, 42, false);
+//!     let z = PyArray1::<f64>::zeros(py, 42, false);
 //!
 //!     // Will work as the three arrays are distinct.
 //!     add(&x, &y, &z);
@@ -63,16 +63,17 @@
 //!
 //! ```rust
 //! use numpy::{PyArray1, PyArrayMethods};
-//! use pyo3::{types::{IntoPyDict, PyAnyMethods}, Python};
+//! use pyo3::{types::{IntoPyDict, PyAnyMethods}, Python, ffi::c_str};
 //!
+//! # fn main() -> pyo3::PyResult<()> {
 //! Python::with_gil(|py| {
-//!     let array = PyArray1::arange_bound(py, 0.0, 10.0, 1.0);
-//!     let locals = [("array", array)].into_py_dict_bound(py);
+//!     let array = PyArray1::arange(py, 0.0, 10.0, 1.0);
+//!     let locals = [("array", array)].into_py_dict(py)?;
 //!
-//!     let view1 = py.eval_bound("array[:5]", None, Some(&locals)).unwrap().downcast_into::<PyArray1<f64>>().unwrap();
-//!     let view2 = py.eval_bound("array[5:]", None, Some(&locals)).unwrap().downcast_into::<PyArray1<f64>>().unwrap();
-//!     let view3 = py.eval_bound("array[::2]", None, Some(&locals)).unwrap().downcast_into::<PyArray1<f64>>().unwrap();
-//!     let view4 = py.eval_bound("array[1::2]", None, Some(&locals)).unwrap().downcast_into::<PyArray1<f64>>().unwrap();
+//!     let view1 = py.eval(c_str!("array[:5]"), None, Some(&locals))?.downcast_into::<PyArray1<f64>>()?;
+//!     let view2 = py.eval(c_str!("array[5:]"), None, Some(&locals))?.downcast_into::<PyArray1<f64>>()?;
+//!     let view3 = py.eval(c_str!("array[::2]"), None, Some(&locals))?.downcast_into::<PyArray1<f64>>()?;
+//!     let view4 = py.eval(c_str!("array[1::2]"), None, Some(&locals))?.downcast_into::<PyArray1<f64>>()?;
 //!
 //!     {
 //!         let _view1 = view1.readwrite();
@@ -83,7 +84,9 @@
 //!         let _view3 = view3.readwrite();
 //!         let _view4 = view4.readwrite();
 //!     }
-//! });
+//! #   Ok(())
+//! })
+//! # }
 //! ```
 //!
 //! The third example shows that some views are incorrectly rejected since the borrows are over-approximated.
@@ -92,14 +95,15 @@
 //! # use std::panic::{catch_unwind, AssertUnwindSafe};
 //! #
 //! use numpy::{PyArray2, PyArrayMethods};
-//! use pyo3::{types::{IntoPyDict, PyAnyMethods}, Python};
+//! use pyo3::{types::{IntoPyDict, PyAnyMethods}, Python, ffi::c_str};
 //!
+//! # fn main() -> pyo3::PyResult<()> {
 //! Python::with_gil(|py| {
-//!     let array = PyArray2::<f64>::zeros_bound(py, (10, 10), false);
-//!     let locals = [("array", array)].into_py_dict_bound(py);
+//!     let array = PyArray2::<f64>::zeros(py, (10, 10), false);
+//!     let locals = [("array", array)].into_py_dict(py)?;
 //!
-//!     let view1 = py.eval_bound("array[:, ::3]", None, Some(&locals)).unwrap().downcast_into::<PyArray2<f64>>().unwrap();
-//!     let view2 = py.eval_bound("array[:, 1::3]", None, Some(&locals)).unwrap().downcast_into::<PyArray2<f64>>().unwrap();
+//!     let view1 = py.eval(c_str!("array[:, ::3]"), None, Some(&locals))?.downcast_into::<PyArray2<f64>>()?;
+//!     let view2 = py.eval(c_str!("array[:, 1::3]"), None, Some(&locals))?.downcast_into::<PyArray2<f64>>()?;
 //!
 //!     // A false conflict as the views do not actually share any elements.
 //!     let res = catch_unwind(AssertUnwindSafe(|| {
@@ -107,7 +111,9 @@
 //!         let _view2 = view2.readwrite();
 //!     }));
 //!     assert!(res.is_err());
-//! });
+//! #   Ok(())
+//! })
+//! # }
 //! ```
 //!
 //! # Rationale
@@ -289,7 +295,7 @@ where
     ///
     /// ```rust
     /// # use pyo3::prelude::*;
-    /// use pyo3::py_run;
+    /// use pyo3::{py_run, ffi::c_str};
     /// use numpy::{get_array_module, PyReadonlyArray2};
     /// use nalgebra::{MatrixView, Const, Dyn};
     ///
@@ -305,17 +311,20 @@ where
     ///     matrix.map(|matrix| matrix.sum())
     /// }
     ///
+    /// # fn main() -> pyo3::PyResult<()> {
     /// Python::with_gil(|py| {
-    ///     let np = py.eval_bound("__import__('numpy')", None, None).unwrap();
-    ///     let sum_standard_layout = wrap_pyfunction!(sum_standard_layout)(py).unwrap();
-    ///     let sum_dynamic_strides = wrap_pyfunction!(sum_dynamic_strides)(py).unwrap();
+    ///     let np = py.eval(c_str!("__import__('numpy')"), None, None)?;
+    ///     let sum_standard_layout = wrap_pyfunction!(sum_standard_layout)(py)?;
+    ///     let sum_dynamic_strides = wrap_pyfunction!(sum_dynamic_strides)(py)?;
     ///
     ///     py_run!(py, np sum_standard_layout, r"assert sum_standard_layout(np.ones((2, 2), order='F')) == 4.");
     ///     py_run!(py, np sum_standard_layout, r"assert sum_standard_layout(np.ones((2, 2, 2))[:,:,0]) is None");
     ///
     ///     py_run!(py, np sum_dynamic_strides, r"assert sum_dynamic_strides(np.ones((2, 2), order='F')) == 4.");
     ///     py_run!(py, np sum_dynamic_strides, r"assert sum_dynamic_strides(np.ones((2, 2, 2))[:,:,0]) == 4.");
-    /// });
+    /// #   Ok(())
+    /// })
+    /// # }
     /// ```
     #[doc(alias = "nalgebra")]
     pub fn try_as_matrix<R, C, RStride, CStride>(
@@ -596,7 +605,7 @@ where
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let pyarray = PyArray::arange_bound(py, 0, 10, 1);
+    ///     let pyarray = PyArray::arange(py, 0, 10, 1);
     ///     assert_eq!(pyarray.len(), 10);
     ///
     ///     let pyarray = pyarray.readwrite();
@@ -654,11 +663,12 @@ mod tests {
     use pyo3::{types::IntoPyDict, Python};
 
     use crate::array::PyArray1;
+    use pyo3::ffi::c_str;
 
     #[test]
     fn test_debug_formatting() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+            let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
             {
                 let shared = array.readonly();
@@ -684,7 +694,7 @@ mod tests {
     #[should_panic(expected = "AlreadyBorrowed")]
     fn cannot_clone_exclusive_borrow_via_deref() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, (3, 2, 1), false);
+            let array = PyArray::<f64, _>::zeros(py, (3, 2, 1), false);
 
             let exclusive = array.readwrite();
             let _shared = exclusive.clone();
@@ -694,12 +704,12 @@ mod tests {
     #[test]
     fn failed_resize_does_not_double_release() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, 10, false);
+            let array = PyArray::<f64, _>::zeros(py, 10, false);
 
             // The view will make the internal reference check of `PyArray_Resize` fail.
-            let locals = [("array", &array)].into_py_dict_bound(py);
+            let locals = [("array", &array)].into_py_dict(py).unwrap();
             let _view = py
-                .eval_bound("array[:]", None, Some(&locals))
+                .eval(c_str!("array[:]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray1<f64>>()
                 .unwrap();
@@ -712,7 +722,7 @@ mod tests {
     #[test]
     fn ineffective_resize_does_not_conflict() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, 10, false);
+            let array = PyArray::<f64, _>::zeros(py, 10, false);
 
             let exclusive = array.readwrite();
             assert!(exclusive.resize(10).is_ok());
diff --git a/src/borrow/shared.rs b/src/borrow/shared.rs
index 38f8963e5..36860b275 100644
--- a/src/borrow/shared.rs
+++ b/src/borrow/shared.rs
@@ -136,7 +136,7 @@ fn insert_shared<'py>(py: Python<'py>) -> PyResult<*const Shared> {
                 release_mut: release_mut_shared,
             };
 
-            let capsule = PyCapsule::new_bound_with_destructor(
+            let capsule = PyCapsule::new_with_destructor(
                 py,
                 shared,
                 Some(CString::new("_RUST_NUMPY_BORROW_CHECKING_API").unwrap()),
@@ -450,6 +450,7 @@ mod tests {
     use crate::array::{PyArray, PyArray1, PyArray2, PyArray3, PyArrayMethods};
     use crate::convert::IntoPyArray;
     use crate::untyped_array::PyUntypedArrayMethods;
+    use pyo3::ffi::c_str;
 
     fn get_borrow_flags<'py>(py: Python<'py>) -> &'py BorrowFlagsInner {
         let shared = get_or_insert_shared(py).unwrap();
@@ -460,7 +461,7 @@ mod tests {
     #[test]
     fn without_base_object() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+            let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
             let base = unsafe { (*array.as_array_ptr()).base };
             assert!(base.is_null());
@@ -477,7 +478,7 @@ mod tests {
     #[test]
     fn with_base_object() {
         Python::with_gil(|py| {
-            let array = Array::<f64, _>::zeros((1, 2, 3)).into_pyarray_bound(py);
+            let array = Array::<f64, _>::zeros((1, 2, 3)).into_pyarray(py);
 
             let base = unsafe { (*array.as_array_ptr()).base };
             assert!(!base.is_null());
@@ -497,11 +498,11 @@ mod tests {
     #[test]
     fn view_without_base_object() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+            let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
-            let locals = [("array", &array)].into_py_dict_bound(py);
+            let locals = [("array", &array)].into_py_dict(py).unwrap();
             let view = py
-                .eval_bound("array[:,:,0]", None, Some(&locals))
+                .eval(c_str!("array[:,:,0]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray2<f64>>()
                 .unwrap();
@@ -526,11 +527,11 @@ mod tests {
     #[test]
     fn view_with_base_object() {
         Python::with_gil(|py| {
-            let array = Array::<f64, _>::zeros((1, 2, 3)).into_pyarray_bound(py);
+            let array = Array::<f64, _>::zeros((1, 2, 3)).into_pyarray(py);
 
-            let locals = [("array", &array)].into_py_dict_bound(py);
+            let locals = [("array", &array)].into_py_dict(py).unwrap();
             let view = py
-                .eval_bound("array[:,:,0]", None, Some(&locals))
+                .eval(c_str!("array[:,:,0]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray2<f64>>()
                 .unwrap();
@@ -561,11 +562,11 @@ mod tests {
     #[test]
     fn view_of_view_without_base_object() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+            let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
-            let locals = [("array", &array)].into_py_dict_bound(py);
+            let locals = [("array", &array)].into_py_dict(py).unwrap();
             let view1 = py
-                .eval_bound("array[:,:,0]", None, Some(&locals))
+                .eval(c_str!("array[:,:,0]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray2<f64>>()
                 .unwrap();
@@ -574,9 +575,9 @@ mod tests {
                 array.as_ptr().cast::<c_void>()
             );
 
-            let locals = [("view1", &view1)].into_py_dict_bound(py);
+            let locals = [("view1", &view1)].into_py_dict(py).unwrap();
             let view2 = py
-                .eval_bound("view1[:,0]", None, Some(&locals))
+                .eval(c_str!("view1[:,0]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray1<f64>>()
                 .unwrap();
@@ -609,11 +610,11 @@ mod tests {
     #[test]
     fn view_of_view_with_base_object() {
         Python::with_gil(|py| {
-            let array = Array::<f64, _>::zeros((1, 2, 3)).into_pyarray_bound(py);
+            let array = Array::<f64, _>::zeros((1, 2, 3)).into_pyarray(py);
 
-            let locals = [("array", &array)].into_py_dict_bound(py);
+            let locals = [("array", &array)].into_py_dict(py).unwrap();
             let view1 = py
-                .eval_bound("array[:,:,0]", None, Some(&locals))
+                .eval(c_str!("array[:,:,0]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray2<f64>>()
                 .unwrap();
@@ -622,9 +623,9 @@ mod tests {
                 array.as_ptr().cast::<c_void>(),
             );
 
-            let locals = [("view1", &view1)].into_py_dict_bound(py);
+            let locals = [("view1", &view1)].into_py_dict(py).unwrap();
             let view2 = py
-                .eval_bound("view1[:,0]", None, Some(&locals))
+                .eval(c_str!("view1[:,0]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray1<f64>>()
                 .unwrap();
@@ -663,11 +664,11 @@ mod tests {
     #[test]
     fn view_with_negative_strides() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+            let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
-            let locals = [("array", &array)].into_py_dict_bound(py);
+            let locals = [("array", &array)].into_py_dict(py).unwrap();
             let view = py
-                .eval_bound("array[::-1,:,::-1]", None, Some(&locals))
+                .eval(c_str!("array[::-1,:,::-1]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray3<f64>>()
                 .unwrap();
@@ -695,7 +696,7 @@ mod tests {
     #[test]
     fn array_with_zero_dimensions() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, (1, 0, 3), false);
+            let array = PyArray::<f64, _>::zeros(py, (1, 0, 3), false);
 
             let base = unsafe { (*array.as_array_ptr()).base };
             assert!(base.is_null());
@@ -712,11 +713,11 @@ mod tests {
     #[test]
     fn view_with_non_dividing_strides() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, (10, 10), false);
-            let locals = [("array", array)].into_py_dict_bound(py);
+            let array = PyArray::<f64, _>::zeros(py, (10, 10), false);
+            let locals = [("array", array)].into_py_dict(py).unwrap();
 
             let view1 = py
-                .eval_bound("array[:,::3]", None, Some(&locals))
+                .eval(c_str!("array[:,::3]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray2<f64>>()
                 .unwrap();
@@ -727,7 +728,7 @@ mod tests {
             assert_eq!(key1.gcd_strides, 8);
 
             let view2 = py
-                .eval_bound("array[:,1::3]", None, Some(&locals))
+                .eval(c_str!("array[:,1::3]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray2<f64>>()
                 .unwrap();
@@ -738,7 +739,7 @@ mod tests {
             assert_eq!(key2.gcd_strides, 8);
 
             let view3 = py
-                .eval_bound("array[:,::2]", None, Some(&locals))
+                .eval(c_str!("array[:,::2]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray2<f64>>()
                 .unwrap();
@@ -749,7 +750,7 @@ mod tests {
             assert_eq!(key3.gcd_strides, 16);
 
             let view4 = py
-                .eval_bound("array[:,1::2]", None, Some(&locals))
+                .eval(c_str!("array[:,1::2]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray2<f64>>()
                 .unwrap();
@@ -771,8 +772,8 @@ mod tests {
     #[test]
     fn borrow_multiple_arrays() {
         Python::with_gil(|py| {
-            let array1 = PyArray::<f64, _>::zeros_bound(py, 10, false);
-            let array2 = PyArray::<f64, _>::zeros_bound(py, 10, false);
+            let array1 = PyArray::<f64, _>::zeros(py, 10, false);
+            let array2 = PyArray::<f64, _>::zeros(py, 10, false);
 
             let base1 = base_address(py, array1.as_array_ptr());
             let base2 = base_address(py, array2.as_array_ptr());
@@ -816,13 +817,13 @@ mod tests {
     #[test]
     fn borrow_multiple_views() {
         Python::with_gil(|py| {
-            let array = PyArray::<f64, _>::zeros_bound(py, 10, false);
+            let array = PyArray::<f64, _>::zeros(py, 10, false);
             let base = base_address(py, array.as_array_ptr());
 
-            let locals = [("array", array)].into_py_dict_bound(py);
+            let locals = [("array", array)].into_py_dict(py).unwrap();
 
             let view1 = py
-                .eval_bound("array[:5]", None, Some(&locals))
+                .eval(c_str!("array[:5]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray1<f64>>()
                 .unwrap();
@@ -842,7 +843,7 @@ mod tests {
             }
 
             let view2 = py
-                .eval_bound("array[5:]", None, Some(&locals))
+                .eval(c_str!("array[5:]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray1<f64>>()
                 .unwrap();
@@ -865,7 +866,7 @@ mod tests {
             }
 
             let view3 = py
-                .eval_bound("array[5:]", None, Some(&locals))
+                .eval(c_str!("array[5:]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray1<f64>>()
                 .unwrap();
@@ -891,7 +892,7 @@ mod tests {
             }
 
             let view4 = py
-                .eval_bound("array[7:]", None, Some(&locals))
+                .eval(c_str!("array[7:]"), None, Some(&locals))
                 .unwrap()
                 .downcast_into::<PyArray1<f64>>()
                 .unwrap();
diff --git a/src/convert.rs b/src/convert.rs
index a58d5c427..66c557b1b 100644
--- a/src/convert.rs
+++ b/src/convert.rs
@@ -24,7 +24,7 @@ use crate::slice_container::PySliceContainer;
 /// use pyo3::Python;
 ///
 /// Python::with_gil(|py| {
-///     let py_array = vec![1, 2, 3].into_pyarray_bound(py);
+///     let py_array = vec![1, 2, 3].into_pyarray(py);
 ///
 ///     assert_eq!(py_array.readonly().as_slice().unwrap(), &[1, 2, 3]);
 ///
@@ -40,29 +40,25 @@ pub trait IntoPyArray: Sized {
     /// The dimension type of the resulting array.
     type Dim: Dimension;
 
-    /// Deprecated form of [`IntoPyArray::into_pyarray_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `IntoPyArray::into_pyarray_bound` in the future"
-    )]
-    #[cfg(feature = "gil-refs")]
-    fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
-        Self::into_pyarray_bound(self, py).into_gil_ref()
-    }
-
     /// Consumes `self` and moves its data into a NumPy array.
-    fn into_pyarray_bound<'py>(self, py: Python<'py>)
-        -> Bound<'py, PyArray<Self::Item, Self::Dim>>;
+    fn into_pyarray<'py>(self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>>;
+
+    /// Deprecated name for [`IntoPyArray::into_pyarray`].
+    #[deprecated(since = "0.23.0", note = "renamed to `IntoPyArray::into_pyarray`")]
+    #[inline]
+    fn into_pyarray_bound<'py>(
+        self,
+        py: Python<'py>,
+    ) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
+        self.into_pyarray(py)
+    }
 }
 
 impl<T: Element> IntoPyArray for Box<[T]> {
     type Item = T;
     type Dim = Ix1;
 
-    fn into_pyarray_bound<'py>(
-        self,
-        py: Python<'py>,
-    ) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
+    fn into_pyarray<'py>(self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
         let container = PySliceContainer::from(self);
         let dims = Dim([container.len]);
         let strides = [mem::size_of::<T>() as npy_intp];
@@ -78,10 +74,7 @@ impl<T: Element> IntoPyArray for Vec<T> {
     type Item = T;
     type Dim = Ix1;
 
-    fn into_pyarray_bound<'py>(
-        mut self,
-        py: Python<'py>,
-    ) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
+    fn into_pyarray<'py>(mut self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
         let dims = Dim([self.len()]);
         let strides = [mem::size_of::<T>() as npy_intp];
         let data_ptr = self.as_mut_ptr();
@@ -105,11 +98,8 @@ where
     type Item = A;
     type Dim = D;
 
-    fn into_pyarray_bound<'py>(
-        self,
-        py: Python<'py>,
-    ) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
-        PyArray::from_owned_array_bound(py, self)
+    fn into_pyarray<'py>(self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
+        PyArray::from_owned_array(py, self)
     }
 }
 
@@ -124,7 +114,7 @@ where
 /// use pyo3::Python;
 ///
 /// Python::with_gil(|py| {
-///     let py_array = vec![1, 2, 3].to_pyarray_bound(py);
+///     let py_array = vec![1, 2, 3].to_pyarray(py);
 ///
 ///     assert_eq!(py_array.readonly().as_slice().unwrap(), &[1, 2, 3]);
 /// });
@@ -140,7 +130,7 @@ where
 ///
 /// Python::with_gil(|py| {
 ///     let array = arr3(&[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]);
-///     let py_array = array.slice(s![.., 0..1, ..]).to_pyarray_bound(py);
+///     let py_array = array.slice(s![.., 0..1, ..]).to_pyarray(py);
 ///
 ///     assert_eq!(py_array.readonly().as_array(), arr3(&[[[1, 2, 3]], [[7, 8, 9]]]));
 ///     assert!(py_array.is_c_contiguous());
@@ -152,26 +142,23 @@ pub trait ToPyArray {
     /// The dimension type of the resulting array.
     type Dim: Dimension;
 
-    /// Deprecated form of [`ToPyArray::to_pyarray_bound`]
-    #[deprecated(
-        since = "0.21.0",
-        note = "will be replaced by `ToPyArray::to_pyarray_bound` in the future"
-    )]
-    #[cfg(feature = "gil-refs")]
-    fn to_pyarray<'py>(&self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
-        Self::to_pyarray_bound(self, py).into_gil_ref()
-    }
-
     /// Copies the content pointed to by `&self` into a newly allocated NumPy array.
-    fn to_pyarray_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>>;
+    fn to_pyarray<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>>;
+
+    /// Deprecated name for [ToPyArray::to_pyarray`].
+    #[deprecated(since = "0.23.0", note = "renamed to ToPyArray::to_pyarray`")]
+    #[inline]
+    fn to_pyarray_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
+        self.to_pyarray(py)
+    }
 }
 
 impl<T: Element> ToPyArray for [T] {
     type Item = T;
     type Dim = Ix1;
 
-    fn to_pyarray_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
-        PyArray::from_slice_bound(py, self)
+    fn to_pyarray<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
+        PyArray::from_slice(py, self)
     }
 }
 
@@ -184,7 +171,7 @@ where
     type Item = A;
     type Dim = D;
 
-    fn to_pyarray_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
+    fn to_pyarray<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
         let len = self.len();
         match self.order() {
             Some(flag) if A::IS_COPY => {
@@ -200,7 +187,7 @@ where
                 // if the array is not contiguous, copy all elements by `ArrayBase::iter`.
                 let dim = self.raw_dim();
                 unsafe {
-                    let array = PyArray::<A, _>::new_bound(py, dim, false);
+                    let array = PyArray::<A, _>::new(py, dim, false);
                     let mut data_ptr = array.data();
                     for item in self.iter() {
                         data_ptr.write(item.clone_ref(py));
@@ -228,9 +215,9 @@ where
     /// matching the [memory layout][memory-layout] used by [`nalgebra`].
     ///
     /// [memory-layout]: https://nalgebra.org/docs/faq/#what-is-the-memory-layout-of-matrices
-    fn to_pyarray_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
+    fn to_pyarray<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray<Self::Item, Self::Dim>> {
         unsafe {
-            let array = PyArray::<N, _>::new_bound(py, (self.nrows(), self.ncols()), true);
+            let array = PyArray::<N, _>::new(py, (self.nrows(), self.ncols()), true);
             let mut data_ptr = array.data();
             if self.data.is_contiguous() {
                 ptr::copy_nonoverlapping(self.data.ptr(), data_ptr, self.len());
diff --git a/src/datetime.rs b/src/datetime.rs
index 50daa269c..3eef346d8 100644
--- a/src/datetime.rs
+++ b/src/datetime.rs
@@ -11,25 +11,22 @@
 //!
 //! ```
 //! use numpy::{datetime::{units, Datetime, Timedelta}, PyArray1, PyArrayMethods};
-//! use pyo3::{Python, types::PyAnyMethods};
+//! use pyo3::{Python, types::PyAnyMethods, ffi::c_str};
 //! # use pyo3::types::PyDict;
 //!
+//! # fn main() -> pyo3::PyResult<()> {
 //! Python::with_gil(|py| {
 //! #    let locals = py
-//! #        .eval_bound("{ 'np': __import__('numpy') }", None, None)
-//! #        .unwrap()
-//! #        .downcast_into::<PyDict>()
-//! #        .unwrap();
+//! #        .eval(c_str!("{ 'np': __import__('numpy') }"), None, None)?
+//! #        .downcast_into::<PyDict>()?;
 //! #
 //!     let array = py
-//!         .eval_bound(
-//!             "np.array([np.datetime64('2017-04-21')])",
+//!         .eval(
+//!             c_str!("np.array([np.datetime64('2017-04-21')])"),
 //!             None,
 //!             Some(&locals),
-//!         )
-//!         .unwrap()
-//!         .downcast_into::<PyArray1<Datetime<units::Days>>>()
-//!         .unwrap();
+//!         )?
+//!         .downcast_into::<PyArray1<Datetime<units::Days>>>()?;
 //!
 //!     assert_eq!(
 //!         array.get_owned(0).unwrap(),
@@ -37,20 +34,20 @@
 //!     );
 //!
 //!     let array = py
-//!         .eval_bound(
-//!             "np.array([np.datetime64('2022-03-29')]) - np.array([np.datetime64('2017-04-21')])",
+//!         .eval(
+//!             c_str!("np.array([np.datetime64('2022-03-29')]) - np.array([np.datetime64('2017-04-21')])"),
 //!             None,
 //!             Some(&locals),
-//!         )
-//!         .unwrap()
-//!         .downcast_into::<PyArray1<Timedelta<units::Days>>>()
-//!         .unwrap();
+//!         )?
+//!         .downcast_into::<PyArray1<Timedelta<units::Days>>>()?;
 //!
 //!     assert_eq!(
 //!         array.get_owned(0).unwrap(),
 //!         Timedelta::<units::Days>::from(1_803)
 //!     );
-//! });
+//! #   Ok(())
+//! })
+//! # }
 //! ```
 //!
 //! [datetime]: https://numpy.org/doc/stable/reference/arrays.datetime.html
@@ -158,7 +155,7 @@ impl<U: Unit> From<Datetime<U>> for i64 {
 unsafe impl<U: Unit> Element for Datetime<U> {
     const IS_COPY: bool = true;
 
-    fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
+    fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
         static DTYPES: TypeDescriptors = unsafe { TypeDescriptors::new(NPY_TYPES::NPY_DATETIME) };
 
         DTYPES.from_unit(py, U::UNIT)
@@ -195,7 +192,7 @@ impl<U: Unit> From<Timedelta<U>> for i64 {
 unsafe impl<U: Unit> Element for Timedelta<U> {
     const IS_COPY: bool = true;
 
-    fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
+    fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
         static DTYPES: TypeDescriptors = unsafe { TypeDescriptors::new(NPY_TYPES::NPY_TIMEDELTA) };
 
         DTYPES.from_unit(py, U::UNIT)
@@ -256,6 +253,7 @@ mod tests {
     use super::*;
 
     use pyo3::{
+        ffi::c_str,
         py_run,
         types::{PyAnyMethods, PyDict, PyModule},
     };
@@ -266,14 +264,14 @@ mod tests {
     fn from_python_to_rust() {
         Python::with_gil(|py| {
             let locals = py
-                .eval_bound("{ 'np': __import__('numpy') }", None, None)
+                .eval(c_str!("{ 'np': __import__('numpy') }"), None, None)
                 .unwrap()
                 .downcast_into::<PyDict>()
                 .unwrap();
 
             let array = py
-                .eval_bound(
-                    "np.array([np.datetime64('1970-01-01')])",
+                .eval(
+                    c_str!("np.array([np.datetime64('1970-01-01')])"),
                     None,
                     Some(&locals),
                 )
@@ -289,12 +287,12 @@ mod tests {
     #[test]
     fn from_rust_to_python() {
         Python::with_gil(|py| {
-            let array = PyArray1::<Timedelta<units::Minutes>>::zeros_bound(py, 1, false);
+            let array = PyArray1::<Timedelta<units::Minutes>>::zeros(py, 1, false);
 
             *array.readwrite().get_mut(0).unwrap() = Timedelta::<units::Minutes>::from(5);
 
             let np = py
-                .eval_bound("__import__('numpy')", None, None)
+                .eval(c_str!("__import__('numpy')"), None, None)
                 .unwrap()
                 .downcast_into::<PyModule>()
                 .unwrap();
@@ -321,7 +319,7 @@ mod tests {
     fn unit_conversion() {
         #[track_caller]
         fn convert<'py, S: Unit, D: Unit>(py: Python<'py>, expected_value: i64) {
-            let array = PyArray1::<Timedelta<S>>::from_slice_bound(py, &[Timedelta::<S>::from(1)]);
+            let array = PyArray1::<Timedelta<S>>::from_slice(py, &[Timedelta::<S>::from(1)]);
             let array = array.cast::<Timedelta<D>>(false).unwrap();
 
             let value: i64 = array.get_owned(0).unwrap().into();
diff --git a/src/dtype.rs b/src/dtype.rs
index e22ac20ac..fd45bb5ec 100644
--- a/src/dtype.rs
+++ b/src/dtype.rs
@@ -8,14 +8,13 @@ use num_traits::{Bounded, Zero};
 #[cfg(feature = "half")]
 use pyo3::sync::GILOnceCell;
 use pyo3::{
+    conversion::IntoPyObject,
     exceptions::{PyIndexError, PyValueError},
     ffi::{self, PyTuple_Size},
-    pyobject_native_type_extract, pyobject_native_type_named,
+    pyobject_native_type_named,
     types::{PyAnyMethods, PyDict, PyDictMethods, PyTuple, PyType},
-    Borrowed, Bound, Py, PyAny, PyObject, PyResult, PyTypeInfo, Python, ToPyObject,
+    Borrowed, Bound, Py, PyAny, PyObject, PyResult, PyTypeInfo, Python,
 };
-#[cfg(feature = "gil-refs")]
-use pyo3::{AsPyPointer, PyNativeType};
 
 use crate::npyffi::{
     NpyTypes, PyArray_Descr, PyDataType_ALIGNMENT, PyDataType_ELSIZE, PyDataType_FIELDS,
@@ -30,20 +29,21 @@ pub use num_complex::{Complex32, Complex64};
 /// # Example
 ///
 /// ```
-/// use numpy::{dtype_bound, get_array_module, PyArrayDescr, PyArrayDescrMethods};
-/// use numpy::pyo3::{types::{IntoPyDict, PyAnyMethods}, Python};
+/// use numpy::{dtype, get_array_module, PyArrayDescr, PyArrayDescrMethods};
+/// use numpy::pyo3::{types::{IntoPyDict, PyAnyMethods}, Python, ffi::c_str};
 ///
+/// # fn main() -> pyo3::PyResult<()> {
 /// Python::with_gil(|py| {
-///     let locals = [("np", get_array_module(py).unwrap())].into_py_dict_bound(py);
+///     let locals = [("np", get_array_module(py)?)].into_py_dict(py)?;
 ///
 ///     let dt = py
-///         .eval_bound("np.array([1, 2, 3.0]).dtype", Some(&locals), None)
-///         .unwrap()
-///         .downcast_into::<PyArrayDescr>()
-///         .unwrap();
+///         .eval(c_str!("np.array([1, 2, 3.0]).dtype"), Some(&locals), None)?
+///         .downcast_into::<PyArrayDescr>()?;
 ///
-///     assert!(dt.is_equiv_to(&dtype_bound::<f64>(py)));
-/// });
+///     assert!(dt.is_equiv_to(&dtype::<f64>(py)));
+/// #   Ok(())
+/// })
+/// # }
 /// ```
 ///
 /// [dtype]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.html
@@ -60,28 +60,19 @@ unsafe impl PyTypeInfo for PyArrayDescr {
     fn type_object_raw<'py>(py: Python<'py>) -> *mut ffi::PyTypeObject {
         unsafe { PY_ARRAY_API.get_type_object(py, NpyTypes::PyArrayDescr_Type) }
     }
-
-    #[cfg(feature = "gil-refs")]
-    fn is_type_of(ob: &PyAny) -> bool {
-        unsafe { ffi::PyObject_TypeCheck(ob.as_ptr(), Self::type_object_raw(ob.py())) > 0 }
-    }
 }
 
-pyobject_native_type_extract!(PyArrayDescr);
-
 /// Returns the type descriptor ("dtype") for a registered type.
-#[cfg(feature = "gil-refs")]
-#[deprecated(
-    since = "0.21.0",
-    note = "This will be replaced by `dtype_bound` in the future."
-)]
-pub fn dtype<'py, T: Element>(py: Python<'py>) -> &'py PyArrayDescr {
-    T::get_dtype_bound(py).into_gil_ref()
+#[inline]
+pub fn dtype<'py, T: Element>(py: Python<'py>) -> Bound<'py, PyArrayDescr> {
+    T::get_dtype(py)
 }
 
-/// Returns the type descriptor ("dtype") for a registered type.
+/// Deprecated name for [`dtype`].
+#[deprecated(since = "0.23.0", note = "renamed to `dtype`")]
+#[inline]
 pub fn dtype_bound<'py, T: Element>(py: Python<'py>) -> Bound<'py, PyArrayDescr> {
-    T::get_dtype_bound(py)
+    dtype::<T>(py)
 }
 
 impl PyArrayDescr {
@@ -91,11 +82,14 @@ impl PyArrayDescr {
     ///
     /// [dtype]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.html
     #[inline]
-    pub fn new_bound<'py, T: ToPyObject + ?Sized>(
-        py: Python<'py>,
-        ob: &T,
-    ) -> PyResult<Bound<'py, Self>> {
-        fn inner(py: Python<'_>, obj: PyObject) -> PyResult<Bound<'_, PyArrayDescr>> {
+    pub fn new<'a, 'py, T>(py: Python<'py>, ob: T) -> PyResult<Bound<'py, Self>>
+    where
+        T: IntoPyObject<'py>,
+    {
+        fn inner<'py>(
+            py: Python<'py>,
+            obj: Borrowed<'_, 'py, PyAny>,
+        ) -> PyResult<Bound<'py, PyArrayDescr>> {
             let mut descr: *mut PyArray_Descr = ptr::null_mut();
             unsafe {
                 // None is an invalid input here and is not converted to NPY_DEFAULT_TYPE
@@ -105,17 +99,50 @@ impl PyArrayDescr {
             }
         }
 
-        inner(py, ob.to_object(py))
+        inner(
+            py,
+            ob.into_pyobject(py)
+                .map_err(Into::into)?
+                .into_any()
+                .as_borrowed(),
+        )
+    }
+
+    /// Deprecated name for [`PyArrayDescr::new`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArrayDescr::new`")]
+    #[allow(deprecated)]
+    #[inline]
+    pub fn new_bound<'py, T: pyo3::ToPyObject + ?Sized>(
+        py: Python<'py>,
+        ob: &T,
+    ) -> PyResult<Bound<'py, Self>> {
+        Self::new(py, ob.to_object(py))
     }
 
     /// Shortcut for creating a type descriptor of `object` type.
-    pub fn object_bound(py: Python<'_>) -> Bound<'_, Self> {
+    #[inline]
+    pub fn object(py: Python<'_>) -> Bound<'_, Self> {
         Self::from_npy_type(py, NPY_TYPES::NPY_OBJECT)
     }
 
+    /// Deprecated name for [`PyArrayDescr::object`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArrayDescr::object`")]
+    #[inline]
+    pub fn object_bound(py: Python<'_>) -> Bound<'_, Self> {
+        Self::object(py)
+    }
+
     /// Returns the type descriptor for a registered type.
+    #[inline]
+    pub fn of<'py, T: Element>(py: Python<'py>) -> Bound<'py, Self> {
+        T::get_dtype(py)
+    }
+
+    /// Deprecated name for [`PyArrayDescr::of`].
+    #[deprecated(since = "0.23.0", note = "renamed to `PyArrayDescr::of`")]
+    #[inline]
     pub fn of_bound<'py, T: Element>(py: Python<'py>) -> Bound<'py, Self> {
-        T::get_dtype_bound(py)
+        Self::of::<T>(py)
     }
 
     fn from_npy_type<'py>(py: Python<'py>, npy_type: NPY_TYPES) -> Bound<'py, Self> {
@@ -133,237 +160,6 @@ impl PyArrayDescr {
     }
 }
 
-#[cfg(feature = "gil-refs")]
-impl PyArrayDescr {
-    /// Creates a new type descriptor ("dtype") object from an arbitrary object.
-    ///
-    /// Equivalent to invoking the constructor of [`numpy.dtype`][dtype].
-    ///
-    /// [dtype]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.html
-    #[deprecated(
-        since = "0.21.0",
-        note = "This will be replace by `new_bound` in the future."
-    )]
-    pub fn new<'py, T: ToPyObject + ?Sized>(py: Python<'py>, ob: &T) -> PyResult<&'py Self> {
-        Self::new_bound(py, ob).map(Bound::into_gil_ref)
-    }
-
-    /// Returns `self` as `*mut PyArray_Descr`.
-    pub fn as_dtype_ptr(&self) -> *mut PyArray_Descr {
-        self.as_borrowed().as_dtype_ptr()
-    }
-
-    /// Returns `self` as `*mut PyArray_Descr` while increasing the reference count.
-    ///
-    /// Useful in cases where the descriptor is stolen by the API.
-    pub fn into_dtype_ptr(&self) -> *mut PyArray_Descr {
-        self.as_borrowed().to_owned().into_dtype_ptr()
-    }
-
-    /// Shortcut for creating a type descriptor of `object` type.
-    #[deprecated(
-        since = "0.21.0",
-        note = "This will be replaced by `object_bound` in the future."
-    )]
-    pub fn object<'py>(py: Python<'py>) -> &'py Self {
-        Self::object_bound(py).into_gil_ref()
-    }
-
-    /// Returns the type descriptor for a registered type.
-    #[deprecated(
-        since = "0.21.0",
-        note = "This will be replaced by `of_bound` in the future."
-    )]
-    pub fn of<'py, T: Element>(py: Python<'py>) -> &'py Self {
-        Self::of_bound::<T>(py).into_gil_ref()
-    }
-
-    /// Returns true if two type descriptors are equivalent.
-    pub fn is_equiv_to(&self, other: &Self) -> bool {
-        self.as_borrowed().is_equiv_to(&other.as_borrowed())
-    }
-
-    /// Returns the [array scalar][arrays-scalars] corresponding to this type descriptor.
-    ///
-    /// Equivalent to [`numpy.dtype.type`][dtype-type].
-    ///
-    /// [arrays-scalars]: https://numpy.org/doc/stable/reference/arrays.scalars.html
-    /// [dtype-type]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.type.html
-    pub fn typeobj(&self) -> &PyType {
-        self.as_borrowed().typeobj().into_gil_ref()
-    }
-
-    /// Returns a unique number for each of the 21 different built-in
-    /// [enumerated types][enumerated-types].
-    ///
-    /// These are roughly ordered from least-to-most precision.
-    ///
-    /// Equivalent to [`numpy.dtype.num`][dtype-num].
-    ///
-    /// [enumerated-types]: https://numpy.org/doc/stable/reference/c-api/dtype.html#enumerated-types
-    /// [dtype-num]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.num.html
-    pub fn num(&self) -> c_int {
-        self.as_borrowed().num()
-    }
-
-    /// Returns the element size of this type descriptor.
-    ///
-    /// Equivalent to [`numpy.dtype.itemsize`][dtype-itemsize].
-    ///
-    /// [dtype-itemsiize]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.itemsize.html
-    pub fn itemsize(&self) -> usize {
-        self.as_borrowed().itemsize()
-    }
-
-    /// Returns the required alignment (bytes) of this type descriptor according to the compiler.
-    ///
-    /// Equivalent to [`numpy.dtype.alignment`][dtype-alignment].
-    ///
-    /// [dtype-alignment]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.alignment.html
-    pub fn alignment(&self) -> usize {
-        self.as_borrowed().alignment()
-    }
-
-    /// Returns an ASCII character indicating the byte-order of this type descriptor object.
-    ///
-    /// All built-in data-type objects have byteorder either `=` or `|`.
-    ///
-    /// Equivalent to [`numpy.dtype.byteorder`][dtype-byteorder].
-    ///
-    /// [dtype-byteorder]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.byteorder.html
-    pub fn byteorder(&self) -> u8 {
-        self.as_borrowed().byteorder()
-    }
-
-    /// Returns a unique ASCII character for each of the 21 different built-in types.
-    ///
-    /// Note that structured data types are categorized as `V` (void).
-    ///
-    /// Equivalent to [`numpy.dtype.char`][dtype-char].
-    ///
-    /// [dtype-char]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.char.html
-    pub fn char(&self) -> u8 {
-        self.as_borrowed().char()
-    }
-
-    /// Returns an ASCII character (one of `biufcmMOSUV`) identifying the general kind of data.
-    ///
-    /// Note that structured data types are categorized as `V` (void).
-    ///
-    /// Equivalent to [`numpy.dtype.kind`][dtype-kind].
-    ///
-    /// [dtype-kind]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.kind.html
-    pub fn kind(&self) -> u8 {
-        self.as_borrowed().kind()
-    }
-
-    /// Returns bit-flags describing how this type descriptor is to be interpreted.
-    ///
-    /// Equivalent to [`numpy.dtype.flags`][dtype-flags].
-    ///
-    /// In numpy 2 the flags field was widened to allow for more flags.
-    ///
-    /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html
-    /// [dtype-changes]: https://numpy.org/devdocs/numpy_2_0_migration_guide.html#the-pyarray-descr-struct-has-been-changed
-    pub fn flags(&self) -> u64 {
-        self.as_borrowed().flags()
-    }
-
-    /// Returns the number of dimensions if this type descriptor represents a sub-array, and zero otherwise.
-    ///
-    /// Equivalent to [`numpy.dtype.ndim`][dtype-ndim].
-    ///
-    /// [dtype-ndim]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.ndim.html
-    pub fn ndim(&self) -> usize {
-        self.as_borrowed().ndim()
-    }
-
-    /// Returns the type descriptor for the base element of subarrays, regardless of their dimension or shape.
-    ///
-    /// If the dtype is not a subarray, returns self.
-    ///
-    /// Equivalent to [`numpy.dtype.base`][dtype-base].
-    ///
-    /// [dtype-base]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.base.html
-    pub fn base(&self) -> &PyArrayDescr {
-        self.as_borrowed().base().into_gil_ref()
-    }
-
-    /// Returns the shape of the sub-array.
-    ///
-    /// If the dtype is not a sub-array, an empty vector is returned.
-    ///
-    /// Equivalent to [`numpy.dtype.shape`][dtype-shape].
-    ///
-    /// [dtype-shape]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.shape.html
-    pub fn shape(&self) -> Vec<usize> {
-        self.as_borrowed().shape()
-    }
-
-    /// Returns true if the type descriptor contains any reference-counted objects in any fields or sub-dtypes.
-    ///
-    /// Equivalent to [`numpy.dtype.hasobject`][dtype-hasobject].
-    ///
-    /// [dtype-hasobject]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.hasobject.html
-    pub fn has_object(&self) -> bool {
-        self.as_borrowed().has_object()
-    }
-
-    /// Returns true if the type descriptor is a struct which maintains field alignment.
-    ///
-    /// This flag is sticky, so when combining multiple structs together, it is preserved
-    /// and produces new dtypes which are also aligned.
-    ///
-    /// Equivalent to [`numpy.dtype.isalignedstruct`][dtype-isalignedstruct].
-    ///
-    /// [dtype-isalignedstruct]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.isalignedstruct.html
-    pub fn is_aligned_struct(&self) -> bool {
-        self.as_borrowed().is_aligned_struct()
-    }
-
-    /// Returns true if the type descriptor is a sub-array.
-    pub fn has_subarray(&self) -> bool {
-        self.as_borrowed().has_subarray()
-    }
-
-    /// Returns true if the type descriptor is a structured type.
-    pub fn has_fields(&self) -> bool {
-        self.as_borrowed().has_fields()
-    }
-
-    /// Returns true if type descriptor byteorder is native, or `None` if not applicable.
-    pub fn is_native_byteorder(&self) -> Option<bool> {
-        self.as_borrowed().is_native_byteorder()
-    }
-
-    /// Returns an ordered list of field names, or `None` if there are no fields.
-    ///
-    /// The names are ordered according to increasing byte offset.
-    ///
-    /// Equivalent to [`numpy.dtype.names`][dtype-names].
-    ///
-    /// [dtype-names]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.names.html
-    pub fn names(&self) -> Option<Vec<String>> {
-        self.as_borrowed().names()
-    }
-
-    /// Returns the type descriptor and offset of the field with the given name.
-    ///
-    /// This method will return an error if this type descriptor is not structured,
-    /// or if it does not contain a field with a given name.
-    ///
-    /// The list of all names can be found via [`PyArrayDescr::names`].
-    ///
-    /// Equivalent to retrieving a single item from [`numpy.dtype.fields`][dtype-fields].
-    ///
-    /// [dtype-fields]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.fields.html
-    pub fn get_field(&self, name: &str) -> PyResult<(&PyArrayDescr, usize)> {
-        self.as_borrowed()
-            .get_field(name)
-            .map(|(descr, n)| (descr.into_gil_ref(), n))
-    }
-}
-
 /// Implementation of functionality for [`PyArrayDescr`].
 #[doc(alias = "PyArrayDescr")]
 pub trait PyArrayDescrMethods<'py>: Sealed {
@@ -396,7 +192,7 @@ pub trait PyArrayDescrMethods<'py>: Sealed {
     /// [enumerated-types]: https://numpy.org/doc/stable/reference/c-api/dtype.html#enumerated-types
     /// [dtype-num]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.num.html
     fn num(&self) -> c_int {
-        unsafe { *self.as_dtype_ptr() }.type_num
+        unsafe { &*self.as_dtype_ptr() }.type_num
     }
 
     /// Returns the element size of this type descriptor.
@@ -421,7 +217,7 @@ pub trait PyArrayDescrMethods<'py>: Sealed {
     ///
     /// [dtype-byteorder]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.byteorder.html
     fn byteorder(&self) -> u8 {
-        unsafe { *self.as_dtype_ptr() }.byteorder.max(0) as _
+        unsafe { &*self.as_dtype_ptr() }.byteorder.max(0) as _
     }
 
     /// Returns a unique ASCII character for each of the 21 different built-in types.
@@ -432,7 +228,7 @@ pub trait PyArrayDescrMethods<'py>: Sealed {
     ///
     /// [dtype-char]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.char.html
     fn char(&self) -> u8 {
-        unsafe { *self.as_dtype_ptr() }.type_.max(0) as _
+        unsafe { &*self.as_dtype_ptr() }.type_.max(0) as _
     }
 
     /// Returns an ASCII character (one of `biufcmMOSUV`) identifying the general kind of data.
@@ -443,7 +239,7 @@ pub trait PyArrayDescrMethods<'py>: Sealed {
     ///
     /// [dtype-kind]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.kind.html
     fn kind(&self) -> u8 {
-        unsafe { *self.as_dtype_ptr() }.kind.max(0) as _
+        unsafe { &*self.as_dtype_ptr() }.kind.max(0) as _
     }
 
     /// Returns bit-flags describing how this type descriptor is to be interpreted.
@@ -567,7 +363,7 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> {
     }
 
     fn typeobj(&self) -> Bound<'py, PyType> {
-        let dtype_type_ptr = unsafe { *self.as_dtype_ptr() }.typeobj;
+        let dtype_type_ptr = unsafe { &*self.as_dtype_ptr() }.typeobj;
         unsafe { PyType::from_borrowed_type_ptr(self.py(), dtype_type_ptr) }
     }
 
@@ -708,17 +504,14 @@ pub unsafe trait Element: Sized + Send + Sync {
     const IS_COPY: bool;
 
     /// Returns the associated type descriptor ("dtype") for the given element type.
-    #[cfg(feature = "gil-refs")]
-    #[deprecated(
-        since = "0.21.0",
-        note = "This will be replaced by `get_dtype_bound` in the future."
-    )]
-    fn get_dtype<'py>(py: Python<'py>) -> &'py PyArrayDescr {
-        Self::get_dtype_bound(py).into_gil_ref()
-    }
+    fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr>;
 
-    /// Returns the associated type descriptor ("dtype") for the given element type.
-    fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr>;
+    /// Deprecated name for [`Element::get_dtype`].
+    #[deprecated(since = "0.23.0", note = "renamed to `Element::get_dtype`")]
+    #[inline]
+    fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
+        Self::get_dtype(py)
+    }
 
     /// Create a clone of the value while the GIL is guaranteed to be held.
     fn clone_ref(&self, py: Python<'_>) -> Self;
@@ -820,6 +613,7 @@ macro_rules! clone_methods_impl {
     };
 }
 pub(crate) use clone_methods_impl;
+use pyo3::BoundObject;
 
 macro_rules! impl_element_scalar {
     (@impl: $ty:ty, $npy_type:expr $(,#[$meta:meta])*) => {
@@ -827,7 +621,7 @@ macro_rules! impl_element_scalar {
         unsafe impl Element for $ty {
             const IS_COPY: bool = true;
 
-            fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
+            fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
                 PyArrayDescr::from_npy_type(py, $npy_type)
             }
 
@@ -857,12 +651,12 @@ impl_element_scalar!(f16 => NPY_HALF);
 unsafe impl Element for bf16 {
     const IS_COPY: bool = true;
 
-    fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
+    fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
         static DTYPE: GILOnceCell<Py<PyArrayDescr>> = GILOnceCell::new();
 
         DTYPE
             .get_or_init(py, || {
-                PyArrayDescr::new_bound(py, "bfloat16").expect("A package which provides a `bfloat16` data type for NumPy is required to use the `half::bf16` element type.").unbind()
+                PyArrayDescr::new(py, "bfloat16").expect("A package which provides a `bfloat16` data type for NumPy is required to use the `half::bf16` element type.").unbind()
             })
             .clone_ref(py)
             .into_bound(py)
@@ -882,8 +676,8 @@ impl_element_scalar!(usize, isize);
 unsafe impl Element for PyObject {
     const IS_COPY: bool = false;
 
-    fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
-        PyArrayDescr::object_bound(py)
+    fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
+        PyArrayDescr::object(py)
     }
 
     #[inline]
@@ -904,28 +698,24 @@ mod tests {
     #[test]
     fn test_dtype_new() {
         Python::with_gil(|py| {
-            assert!(PyArrayDescr::new_bound(py, "float64")
+            assert!(PyArrayDescr::new(py, "float64")
                 .unwrap()
-                .is(&dtype_bound::<f64>(py)));
+                .is(&dtype::<f64>(py)));
 
-            let dt = PyArrayDescr::new_bound(py, [("a", "O"), ("b", "?")].as_ref()).unwrap();
+            let dt = PyArrayDescr::new(py, [("a", "O"), ("b", "?")].as_ref()).unwrap();
             assert_eq!(dt.names(), Some(vec!["a".to_owned(), "b".to_owned()]));
             assert!(dt.has_object());
-            assert!(dt
-                .get_field("a")
-                .unwrap()
-                .0
-                .is(&dtype_bound::<PyObject>(py)));
-            assert!(dt.get_field("b").unwrap().0.is(&dtype_bound::<bool>(py)));
+            assert!(dt.get_field("a").unwrap().0.is(&dtype::<PyObject>(py)));
+            assert!(dt.get_field("b").unwrap().0.is(&dtype::<bool>(py)));
 
-            assert!(PyArrayDescr::new_bound(py, &123_usize).is_err());
+            assert!(PyArrayDescr::new(py, 123_usize).is_err());
         });
     }
 
     #[test]
     fn test_dtype_names() {
         fn type_name<T: Element>(py: Python<'_>) -> Bound<'_, PyString> {
-            dtype_bound::<T>(py).typeobj().qualname().unwrap()
+            dtype::<T>(py).typeobj().qualname().unwrap()
         }
         Python::with_gil(|py| {
             if is_numpy_2(py) {
@@ -965,7 +755,7 @@ mod tests {
     #[test]
     fn test_dtype_methods_scalar() {
         Python::with_gil(|py| {
-            let dt = dtype_bound::<f64>(py);
+            let dt = dtype::<f64>(py);
 
             assert_eq!(dt.num(), NPY_TYPES::NPY_DOUBLE as c_int);
             assert_eq!(dt.flags(), 0);
@@ -983,14 +773,14 @@ mod tests {
             assert!(!dt.has_subarray());
             assert!(dt.base().is_equiv_to(&dt));
             assert_eq!(dt.ndim(), 0);
-            assert_eq!(dt.shape(), vec![]);
+            assert_eq!(dt.shape(), Vec::<usize>::new());
         });
     }
 
     #[test]
     fn test_dtype_methods_subarray() {
         Python::with_gil(|py| {
-            let locals = PyDict::new_bound(py);
+            let locals = PyDict::new(py);
             py_run!(
                 py,
                 *locals,
@@ -1019,14 +809,14 @@ mod tests {
             assert!(dt.has_subarray());
             assert_eq!(dt.ndim(), 2);
             assert_eq!(dt.shape(), vec![2, 3]);
-            assert!(dt.base().is_equiv_to(&dtype_bound::<f64>(py)));
+            assert!(dt.base().is_equiv_to(&dtype::<f64>(py)));
         });
     }
 
     #[test]
     fn test_dtype_methods_record() {
         Python::with_gil(|py| {
-            let locals = PyDict::new_bound(py);
+            let locals = PyDict::new(py);
             py_run!(
                 py,
                 *locals,
@@ -1059,16 +849,16 @@ mod tests {
             assert!(dt.is_aligned_struct());
             assert!(!dt.has_subarray());
             assert_eq!(dt.ndim(), 0);
-            assert_eq!(dt.shape(), vec![]);
+            assert_eq!(dt.shape(), Vec::<usize>::new());
             assert!(dt.base().is_equiv_to(&dt));
             let x = dt.get_field("x").unwrap();
-            assert!(x.0.is_equiv_to(&dtype_bound::<u8>(py)));
+            assert!(x.0.is_equiv_to(&dtype::<u8>(py)));
             assert_eq!(x.1, 0);
             let y = dt.get_field("y").unwrap();
-            assert!(y.0.is_equiv_to(&dtype_bound::<f64>(py)));
+            assert!(y.0.is_equiv_to(&dtype::<f64>(py)));
             assert_eq!(y.1, 8);
             let z = dt.get_field("z").unwrap();
-            assert!(z.0.is_equiv_to(&dtype_bound::<PyObject>(py)));
+            assert!(z.0.is_equiv_to(&dtype::<PyObject>(py)));
             assert_eq!(z.1, 16);
         });
     }
diff --git a/src/error.rs b/src/error.rs
index 26b91fef6..7293488fe 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -4,7 +4,8 @@ use std::error::Error;
 use std::fmt;
 
 use pyo3::{
-    exceptions::PyTypeError, Bound, Py, PyErr, PyErrArguments, PyObject, Python, ToPyObject,
+    conversion::IntoPyObject, exceptions::PyTypeError, Bound, Py, PyErr, PyErrArguments, PyObject,
+    Python,
 };
 
 use crate::dtype::PyArrayDescr;
@@ -22,7 +23,11 @@ macro_rules! impl_pyerr {
 
         impl PyErrArguments for $err_type {
             fn arguments<'py>(self, py: Python<'py>) -> PyObject {
-                self.to_string().to_object(py)
+                self.to_string()
+                    .into_pyobject(py)
+                    .unwrap()
+                    .into_any()
+                    .unbind()
             }
         }
 
@@ -92,7 +97,11 @@ impl PyErrArguments for TypeErrorArguments {
             to: self.to.into_bound(py),
         };
 
-        err.to_string().to_object(py)
+        err.to_string()
+            .into_pyobject(py)
+            .unwrap()
+            .into_any()
+            .unbind()
     }
 }
 
diff --git a/src/lib.rs b/src/lib.rs
index 6b72c2151..e147a9c18 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -26,7 +26,7 @@ as well as the [`PyReadonlyArray::try_as_matrix`] and [`PyReadwriteArray::try_as
 //! use numpy::{ToPyArray, PyArray, PyArrayMethods};
 //!
 //! Python::with_gil(|py| {
-//!     let py_array = array![[1i64, 2], [3, 4]].to_pyarray_bound(py);
+//!     let py_array = array![[1i64, 2], [3, 4]].to_pyarray(py);
 //!
 //!     assert_eq!(
 //!         py_array.readonly().as_array(),
@@ -39,10 +39,10 @@ as well as the [`PyReadonlyArray::try_as_matrix`] and [`PyReadwriteArray::try_as
 #![cfg_attr(not(feature = "nalgebra"), doc = "```rust,ignore")]
 //! use numpy::pyo3::Python;
 //! use numpy::nalgebra::Matrix3;
-//! use numpy::{pyarray_bound, ToPyArray, PyArrayMethods};
+//! use numpy::{pyarray, ToPyArray, PyArrayMethods};
 //!
 //! Python::with_gil(|py| {
-//!     let py_array = pyarray_bound![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]];
+//!     let py_array = pyarray![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]];
 //!
 //!     let py_array_square;
 //!
@@ -70,9 +70,6 @@ as well as the [`PyReadonlyArray::try_as_matrix`] and [`PyReadwriteArray::try_as
 //! [c-api]: https://numpy.org/doc/stable/reference/c-api
 //! [ndarray]: https://numpy.org/doc/stable/reference/arrays.ndarray.html
 #![deny(missing_docs)]
-// requiring `Debug` impls is not relevant without gil-refs since `&PyArray`
-// and similar aren't constructible
-#![cfg_attr(feature = "gil-refs", deny(missing_debug_implementations))]
 
 pub mod array;
 mod array_like;
@@ -108,18 +105,14 @@ pub use crate::borrow::{
     PyReadwriteArray5, PyReadwriteArray6, PyReadwriteArrayDyn,
 };
 pub use crate::convert::{IntoPyArray, NpyIndex, ToNpyDims, ToPyArray};
-#[cfg(feature = "gil-refs")]
 #[allow(deprecated)]
-pub use crate::dtype::dtype;
-pub use crate::dtype::{
-    dtype_bound, Complex32, Complex64, Element, PyArrayDescr, PyArrayDescrMethods,
-};
+pub use crate::dtype::dtype_bound;
+pub use crate::dtype::{dtype, Complex32, Complex64, Element, PyArrayDescr, PyArrayDescrMethods};
 pub use crate::error::{BorrowError, FromVecError, NotContiguousError};
 pub use crate::npyffi::{PY_ARRAY_API, PY_UFUNC_API};
 pub use crate::strings::{PyFixedString, PyFixedUnicode};
-#[cfg(feature = "gil-refs")]
-#[allow(deprecated)]
 pub use crate::sum_products::{dot, einsum, inner};
+#[allow(deprecated)]
 pub use crate::sum_products::{dot_bound, einsum_bound, inner_bound};
 pub use crate::untyped_array::{PyUntypedArray, PyUntypedArrayMethods};
 
@@ -156,27 +149,6 @@ mod doctest {
 #[inline(always)]
 fn cold() {}
 
-/// Deprecated form of [`pyarray_bound`]
-#[deprecated(
-    since = "0.21.0",
-    note = "will be replace by `pyarray_bound` in the future"
-)]
-#[macro_export]
-macro_rules! pyarray {
-    ($py: ident, $([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*) => {{
-        #[allow(deprecated)]
-        $crate::IntoPyArray::into_pyarray($crate::array![$([$([$($x,)*],)*],)*], $py)
-    }};
-    ($py: ident, $([$($x:expr),* $(,)*]),+ $(,)*) => {{
-        #[allow(deprecated)]
-        $crate::IntoPyArray::into_pyarray($crate::array![$([$($x,)*],)*], $py)
-    }};
-    ($py: ident, $($x:expr),* $(,)*) => {{
-        #[allow(deprecated)]
-        $crate::IntoPyArray::into_pyarray($crate::array![$($x,)*], $py)
-    }};
-}
-
 /// Create a [`PyArray`] with one, two or three dimensions.
 ///
 /// This macro is backed by [`ndarray::array`].
@@ -186,10 +158,10 @@ macro_rules! pyarray {
 /// ```
 /// use numpy::pyo3::Python;
 /// use numpy::ndarray::array;
-/// use numpy::{pyarray_bound, PyArrayMethods};
+/// use numpy::{pyarray, PyArrayMethods};
 ///
 /// Python::with_gil(|py| {
-///     let array = pyarray_bound![py, [1, 2], [3, 4]];
+///     let array = pyarray![py, [1, 2], [3, 4]];
 ///
 ///     assert_eq!(
 ///         array.readonly().as_array(),
@@ -197,14 +169,29 @@ macro_rules! pyarray {
 ///     );
 /// });
 #[macro_export]
+macro_rules! pyarray {
+    ($py: ident, $([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*) => {{
+        $crate::IntoPyArray::into_pyarray($crate::array![$([$([$($x,)*],)*],)*], $py)
+    }};
+    ($py: ident, $([$($x:expr),* $(,)*]),+ $(,)*) => {{
+        $crate::IntoPyArray::into_pyarray($crate::array![$([$($x,)*],)*], $py)
+    }};
+    ($py: ident, $($x:expr),* $(,)*) => {{
+        $crate::IntoPyArray::into_pyarray($crate::array![$($x,)*], $py)
+    }};
+}
+
+/// Deprecated name for [`pyarray`].
+#[deprecated(since = "0.23.0", note = "renamed to `pyarray`")]
+#[macro_export]
 macro_rules! pyarray_bound {
     ($py: ident, $([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*) => {{
-        $crate::IntoPyArray::into_pyarray_bound($crate::array![$([$([$($x,)*],)*],)*], $py)
+        $crate::IntoPyArray::into_pyarray($crate::array![$([$([$($x,)*],)*],)*], $py)
     }};
     ($py: ident, $([$($x:expr),* $(,)*]),+ $(,)*) => {{
-        $crate::IntoPyArray::into_pyarray_bound($crate::array![$([$($x,)*],)*], $py)
+        $crate::IntoPyArray::into_pyarray($crate::array![$([$($x,)*],)*], $py)
     }};
     ($py: ident, $($x:expr),* $(,)*) => {{
-        $crate::IntoPyArray::into_pyarray_bound($crate::array![$($x,)*], $py)
+        $crate::IntoPyArray::into_pyarray($crate::array![$($x,)*], $py)
     }};
 }
diff --git a/src/npyffi/array.rs b/src/npyffi/array.rs
index 9689e89df..55fe64ded 100644
--- a/src/npyffi/array.rs
+++ b/src/npyffi/array.rs
@@ -23,10 +23,10 @@ pub(crate) fn numpy_core_name(py: Python<'_>) -> PyResult<&'static str> {
 
             // strategy mirrored from https://github.com/pybind/pybind11/blob/af67e87393b0f867ccffc2702885eea12de063fc/include/pybind11/numpy.h#L175-L195
 
-            let numpy = PyModule::import_bound(py, "numpy")?;
+            let numpy = PyModule::import(py, "numpy")?;
             let version_string = numpy.getattr("__version__")?;
 
-            let numpy_lib = PyModule::import_bound(py, "numpy.lib")?;
+            let numpy_lib = PyModule::import(py, "numpy.lib")?;
             let numpy_version = numpy_lib
                 .getattr("NumpyVersion")?
                 .call1((version_string,))?;
@@ -65,7 +65,7 @@ const CAPSULE_NAME: &str = "_ARRAY_API";
 /// use numpy::prelude::*;
 /// use numpy::{PyArray, npyffi::types::NPY_SORTKIND, PY_ARRAY_API};
 /// pyo3::Python::with_gil(|py| {
-///     let array = PyArray::from_slice_bound(py, &[3, 2, 4]);
+///     let array = PyArray::from_slice(py, &[3, 2, 4]);
 ///     unsafe {
 ///         PY_ARRAY_API.PyArray_Sort(py, array.as_array_ptr(), 0, NPY_SORTKIND::NPY_QUICKSORT);
 ///     }
diff --git a/src/npyffi/mod.rs b/src/npyffi/mod.rs
index 8a75dff8b..b96e69344 100644
--- a/src/npyffi/mod.rs
+++ b/src/npyffi/mod.rs
@@ -27,7 +27,7 @@ fn get_numpy_api<'py>(
     module: &str,
     capsule: &str,
 ) -> PyResult<*const *const c_void> {
-    let module = PyModule::import_bound(py, module)?;
+    let module = PyModule::import(py, module)?;
     let capsule = module.getattr(capsule)?.downcast_into::<PyCapsule>()?;
 
     let api = capsule.pointer() as *const *const c_void;
diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs
index fa0fb403c..d28e88b7e 100644
--- a/src/npyffi/objects.rs
+++ b/src/npyffi/objects.rs
@@ -11,7 +11,6 @@ use super::types::*;
 use crate::npyffi::*;
 
 #[repr(C)]
-#[derive(Copy, Clone)]
 pub struct PyArrayObject {
     pub ob_base: PyObject,
     pub data: *mut c_char,
@@ -25,7 +24,6 @@ pub struct PyArrayObject {
 }
 
 #[repr(C)]
-#[derive(Copy, Clone)]
 pub struct PyArray_Descr {
     pub ob_base: PyObject,
     pub typeobj: *mut PyTypeObject,
@@ -37,7 +35,6 @@ pub struct PyArray_Descr {
 }
 
 #[repr(C)]
-#[derive(Copy, Clone)]
 pub struct PyArray_DescrProto {
     pub ob_base: PyObject,
     pub typeobj: *mut PyTypeObject,
@@ -58,7 +55,6 @@ pub struct PyArray_DescrProto {
 }
 
 #[repr(C)]
-#[derive(Copy, Clone)]
 pub struct _PyArray_DescrNumPy2 {
     pub ob_base: PyObject,
     pub typeobj: *mut PyTypeObject,
@@ -76,7 +72,6 @@ pub struct _PyArray_DescrNumPy2 {
 }
 
 #[repr(C)]
-#[derive(Copy, Clone)]
 struct _PyArray_LegacyDescr {
     pub ob_base: PyObject,
     pub typeobj: *mut PyTypeObject,
@@ -310,7 +305,6 @@ pub type PyArray_FastTakeFunc = Option<
 >;
 
 #[repr(C)]
-#[derive(Clone, Copy)]
 pub struct PyArrayFlagsObject {
     pub ob_base: PyObject,
     pub arr: *mut PyObject,
@@ -325,7 +319,6 @@ pub struct PyArray_Dims {
 }
 
 #[repr(C)]
-#[derive(Clone, Copy)]
 pub struct PyArray_Chunk {
     pub ob_base: PyObject,
     pub base: *mut PyObject,
@@ -349,7 +342,6 @@ pub struct PyArrayInterface {
 }
 
 #[repr(C)]
-#[derive(Clone, Copy)]
 pub struct PyUFuncObject {
     pub ob_base: PyObject,
     pub nin: c_int,
@@ -428,7 +420,6 @@ pub type PyUFunc_MaskedInnerLoopSelectionFunc = Option<
 pub struct NpyIter([u8; 0]);
 
 #[repr(C)]
-#[derive(Clone, Copy)]
 pub struct PyArrayIterObject {
     pub ob_base: PyObject,
     pub nd_m1: c_int,
@@ -449,7 +440,6 @@ pub struct PyArrayIterObject {
 }
 
 #[repr(C)]
-#[derive(Clone, Copy)]
 pub struct PyArrayMultiIterObject {
     pub ob_base: PyObject,
     pub numiter: c_int,
@@ -461,7 +451,6 @@ pub struct PyArrayMultiIterObject {
 }
 
 #[repr(C)]
-#[derive(Clone, Copy)]
 pub struct PyArrayNeighborhoodIterObject {
     pub ob_base: PyObject,
     pub nd_m1: c_int,
@@ -487,7 +476,6 @@ pub struct PyArrayNeighborhoodIterObject {
 }
 
 #[repr(C)]
-#[derive(Clone, Copy)]
 pub struct PyArrayMapIterObject {
     pub ob_base: PyObject,
     pub numiter: c_int,
@@ -571,7 +559,6 @@ pub struct npy_static_string {
 }
 
 #[repr(C)]
-#[derive(Clone, Copy)]
 pub struct PyArray_StringDTypeObject {
     pub base: PyArray_Descr,
     pub na_object: *mut PyObject,
diff --git a/src/strings.rs b/src/strings.rs
index 3e184d144..067327865 100644
--- a/src/strings.rs
+++ b/src/strings.rs
@@ -51,7 +51,7 @@ use crate::npyffi::NPY_TYPES;
 /// use numpy::{PyArray1, PyUntypedArrayMethods, PyFixedString};
 ///
 /// # Python::with_gil(|py| {
-/// let array = PyArray1::<PyFixedString<3>>::from_vec_bound(py, vec![[b'f', b'o', b'o'].into()]);
+/// let array = PyArray1::<PyFixedString<3>>::from_vec(py, vec![[b'f', b'o', b'o'].into()]);
 ///
 /// assert!(array.dtype().to_string().contains("S3"));
 /// # });
@@ -77,7 +77,7 @@ impl<const N: usize> From<[Py_UCS1; N]> for PyFixedString<N> {
 unsafe impl<const N: usize> Element for PyFixedString<N> {
     const IS_COPY: bool = true;
 
-    fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
+    fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
         static DTYPES: TypeDescriptors = TypeDescriptors::new();
 
         unsafe { DTYPES.from_size(py, NPY_TYPES::NPY_STRING, b'|' as _, size_of::<Self>()) }
@@ -116,7 +116,7 @@ unsafe impl<const N: usize> Element for PyFixedString<N> {
 /// use numpy::{PyArray1, PyUntypedArrayMethods, PyFixedUnicode};
 ///
 /// # Python::with_gil(|py| {
-/// let array = PyArray1::<PyFixedUnicode<3>>::from_vec_bound(py, vec![[b'b' as _, b'a' as _, b'r' as _].into()]);
+/// let array = PyArray1::<PyFixedUnicode<3>>::from_vec(py, vec![[b'b' as _, b'a' as _, b'r' as _].into()]);
 ///
 /// assert!(array.dtype().to_string().contains("U3"));
 /// # });
@@ -150,7 +150,7 @@ impl<const N: usize> From<[Py_UCS4; N]> for PyFixedUnicode<N> {
 unsafe impl<const N: usize> Element for PyFixedUnicode<N> {
     const IS_COPY: bool = true;
 
-    fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
+    fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
         static DTYPES: TypeDescriptors = TypeDescriptors::new();
 
         unsafe { DTYPES.from_size(py, NPY_TYPES::NPY_UNICODE, b'=' as _, size_of::<Self>()) }
diff --git a/src/sum_products.rs b/src/sum_products.rs
index 0101f7212..8aadbdc66 100644
--- a/src/sum_products.rs
+++ b/src/sum_products.rs
@@ -4,8 +4,6 @@ use std::ptr::null_mut;
 
 use ndarray::{Dimension, IxDyn};
 use pyo3::types::PyAnyMethods;
-#[cfg(feature = "gil-refs")]
-use pyo3::PyNativeType;
 use pyo3::{Borrowed, Bound, FromPyObject, PyResult};
 
 use crate::array::PyArray;
@@ -15,14 +13,6 @@ use crate::npyffi::{array::PY_ARRAY_API, NPY_CASTING, NPY_ORDER};
 /// Return value of a function that can yield either an array or a scalar.
 pub trait ArrayOrScalar<'py, T>: FromPyObject<'py> {}
 
-#[cfg(feature = "gil-refs")]
-impl<'py, T, D> ArrayOrScalar<'py, T> for &'py PyArray<T, D>
-where
-    T: Element,
-    D: Dimension,
-{
-}
-
 impl<'py, T, D> ArrayOrScalar<'py, T> for Bound<'py, PyArray<T, D>>
 where
     T: Element,
@@ -32,25 +22,6 @@ where
 
 impl<'py, T> ArrayOrScalar<'py, T> for T where T: Element + FromPyObject<'py> {}
 
-/// Deprecated form of [`inner_bound`]
-#[deprecated(
-    since = "0.21.0",
-    note = "will be replaced by `inner_bound` in the future"
-)]
-#[cfg(feature = "gil-refs")]
-pub fn inner<'py, T, DIN1, DIN2, OUT>(
-    array1: &'py PyArray<T, DIN1>,
-    array2: &'py PyArray<T, DIN2>,
-) -> PyResult<OUT>
-where
-    T: Element,
-    DIN1: Dimension,
-    DIN2: Dimension,
-    OUT: ArrayOrScalar<'py, T>,
-{
-    inner_bound(&array1.as_borrowed(), &array2.as_borrowed())
-}
-
 /// Return the inner product of two arrays.
 ///
 /// [NumPy's documentation][inner] has the details.
@@ -61,11 +32,11 @@ where
 ///
 /// ```
 /// use pyo3::Python;
-/// use numpy::{inner_bound, pyarray_bound, PyArray0};
+/// use numpy::{inner, pyarray, PyArray0};
 ///
 /// Python::with_gil(|py| {
-///     let vector = pyarray_bound![py, 1.0, 2.0, 3.0];
-///     let result: f64 = inner_bound(&vector, &vector).unwrap();
+///     let vector = pyarray![py, 1.0, 2.0, 3.0];
+///     let result: f64 = inner(&vector, &vector).unwrap();
 ///     assert_eq!(result, 14.0);
 /// });
 /// ```
@@ -75,17 +46,17 @@ where
 /// ```
 /// use pyo3::{Python, Bound};
 /// use numpy::prelude::*;
-/// use numpy::{inner_bound, pyarray_bound, PyArray0};
+/// use numpy::{inner, pyarray, PyArray0};
 ///
 /// Python::with_gil(|py| {
-///     let vector = pyarray_bound![py, 1, 2, 3];
-///     let result: Bound<'_, PyArray0<_>> = inner_bound(&vector, &vector).unwrap();
+///     let vector = pyarray![py, 1, 2, 3];
+///     let result: Bound<'_, PyArray0<_>> = inner(&vector, &vector).unwrap();
 ///     assert_eq!(result.item(), 14);
 /// });
 /// ```
 ///
 /// [inner]: https://numpy.org/doc/stable/reference/generated/numpy.inner.html
-pub fn inner_bound<'py, T, DIN1, DIN2, OUT>(
+pub fn inner<'py, T, DIN1, DIN2, OUT>(
     array1: &Bound<'py, PyArray<T, DIN1>>,
     array2: &Bound<'py, PyArray<T, DIN2>>,
 ) -> PyResult<OUT>
@@ -103,15 +74,12 @@ where
     obj.extract()
 }
 
-/// Deprecated form of [`dot_bound`]
-#[cfg(feature = "gil-refs")]
-#[deprecated(
-    since = "0.21.0",
-    note = "will be replaced by `dot_bound` in the future"
-)]
-pub fn dot<'py, T, DIN1, DIN2, OUT>(
-    array1: &'py PyArray<T, DIN1>,
-    array2: &'py PyArray<T, DIN2>,
+/// Deprecated name for [`inner`].
+#[deprecated(since = "0.23.0", note = "renamed to `inner`")]
+#[inline]
+pub fn inner_bound<'py, T, DIN1, DIN2, OUT>(
+    array1: &Bound<'py, PyArray<T, DIN1>>,
+    array2: &Bound<'py, PyArray<T, DIN2>>,
 ) -> PyResult<OUT>
 where
     T: Element,
@@ -119,7 +87,7 @@ where
     DIN2: Dimension,
     OUT: ArrayOrScalar<'py, T>,
 {
-    dot_bound(&array1.as_borrowed(), &array2.as_borrowed())
+    inner(array1, array2)
 }
 
 /// Return the dot product of two arrays.
@@ -133,13 +101,13 @@ where
 /// ```
 /// use pyo3::{Python, Bound};
 /// use ndarray::array;
-/// use numpy::{dot_bound, pyarray_bound, PyArray2, PyArrayMethods};
+/// use numpy::{dot, pyarray, PyArray2, PyArrayMethods};
 ///
 /// Python::with_gil(|py| {
-///     let matrix = pyarray_bound![py, [1, 0], [0, 1]];
-///     let another_matrix = pyarray_bound![py, [4, 1], [2, 2]];
+///     let matrix = pyarray![py, [1, 0], [0, 1]];
+///     let another_matrix = pyarray![py, [4, 1], [2, 2]];
 ///
-///     let result: Bound<'_, PyArray2<_>> = dot_bound(&matrix, &another_matrix).unwrap();
+///     let result: Bound<'_, PyArray2<_>> = dot(&matrix, &another_matrix).unwrap();
 ///
 ///     assert_eq!(
 ///         result.readonly().as_array(),
@@ -152,17 +120,17 @@ where
 ///
 /// ```
 /// use pyo3::Python;
-/// use numpy::{dot_bound, pyarray_bound, PyArray0};
+/// use numpy::{dot, pyarray, PyArray0};
 ///
 /// Python::with_gil(|py| {
-///     let vector = pyarray_bound![py, 1.0, 2.0, 3.0];
-///     let result: f64 = dot_bound(&vector, &vector).unwrap();
+///     let vector = pyarray![py, 1.0, 2.0, 3.0];
+///     let result: f64 = dot(&vector, &vector).unwrap();
 ///     assert_eq!(result, 14.0);
 /// });
 /// ```
 ///
 /// [dot]: https://numpy.org/doc/stable/reference/generated/numpy.dot.html
-pub fn dot_bound<'py, T, DIN1, DIN2, OUT>(
+pub fn dot<'py, T, DIN1, DIN2, OUT>(
     array1: &Bound<'py, PyArray<T, DIN1>>,
     array2: &Bound<'py, PyArray<T, DIN2>>,
 ) -> PyResult<OUT>
@@ -180,28 +148,26 @@ where
     obj.extract()
 }
 
-/// Deprecated form of [`einsum_bound`]
-#[cfg(feature = "gil-refs")]
-#[deprecated(
-    since = "0.21.0",
-    note = "will be replaced by `einsum_bound` in the future"
-)]
-pub fn einsum<'py, T, OUT>(subscripts: &str, arrays: &[&'py PyArray<T, IxDyn>]) -> PyResult<OUT>
+/// Deprecated name for [`dot`].
+#[deprecated(since = "0.23.0", note = "renamed to `dot`")]
+#[inline]
+pub fn dot_bound<'py, T, DIN1, DIN2, OUT>(
+    array1: &Bound<'py, PyArray<T, DIN1>>,
+    array2: &Bound<'py, PyArray<T, DIN2>>,
+) -> PyResult<OUT>
 where
     T: Element,
+    DIN1: Dimension,
+    DIN2: Dimension,
     OUT: ArrayOrScalar<'py, T>,
 {
-    // Safety: &PyArray<T, IxDyn> has the same size and layout in memory as
-    // Borrowed<'_, '_, PyArray<T, IxDyn>>
-    einsum_bound(subscripts, unsafe {
-        std::slice::from_raw_parts(arrays.as_ptr().cast(), arrays.len())
-    })
+    dot(array1, array2)
 }
 
 /// Return the Einstein summation convention of given tensors.
 ///
 /// This is usually invoked via the the [`einsum!`][crate::einsum!] macro.
-pub fn einsum_bound<'py, T, OUT>(
+pub fn einsum<'py, T, OUT>(
     subscripts: &str,
     arrays: &[Borrowed<'_, 'py, PyArray<T, IxDyn>>],
 ) -> PyResult<OUT>
@@ -231,19 +197,18 @@ where
     obj.extract()
 }
 
-/// Deprecated form of [`einsum_bound!`][crate::einsum_bound!]
-#[cfg(feature = "gil-refs")]
-#[deprecated(
-    since = "0.21.0",
-    note = "will be replaced by `einsum_bound!` in the future"
-)]
-#[macro_export]
-macro_rules! einsum {
-    ($subscripts:literal $(,$array:ident)+ $(,)*) => {{
-        use pyo3::PyNativeType;
-        let arrays = [$($array.to_dyn().as_borrowed(),)+];
-        $crate::einsum_bound(concat!($subscripts, "\0"), &arrays)
-    }};
+/// Deprecated name for [`einsum`].
+#[deprecated(since = "0.23.0", note = "renamed to `einsum`")]
+#[inline]
+pub fn einsum_bound<'py, T, OUT>(
+    subscripts: &str,
+    arrays: &[Borrowed<'_, 'py, PyArray<T, IxDyn>>],
+) -> PyResult<OUT>
+where
+    T: Element,
+    OUT: ArrayOrScalar<'py, T>,
+{
+    einsum(subscripts, arrays)
 }
 
 /// Return the Einstein summation convention of given tensors.
@@ -256,13 +221,13 @@ macro_rules! einsum {
 /// ```
 /// use pyo3::{Python, Bound};
 /// use ndarray::array;
-/// use numpy::{einsum_bound, pyarray_bound, PyArray, PyArray2, PyArrayMethods};
+/// use numpy::{einsum, pyarray, PyArray, PyArray2, PyArrayMethods};
 ///
 /// Python::with_gil(|py| {
-///     let tensor = PyArray::arange_bound(py, 0, 2 * 3 * 4, 1).reshape([2, 3, 4]).unwrap();
-///     let another_tensor = pyarray_bound![py, [20, 30], [40, 50], [60, 70]];
+///     let tensor = PyArray::arange(py, 0, 2 * 3 * 4, 1).reshape([2, 3, 4]).unwrap();
+///     let another_tensor = pyarray![py, [20, 30], [40, 50], [60, 70]];
 ///
-///     let result: Bound<'_, PyArray2<_>> = einsum_bound!("ijk,ji->ik", tensor, another_tensor).unwrap();
+///     let result: Bound<'_, PyArray2<_>> = einsum!("ijk,ji->ik", tensor, another_tensor).unwrap();
 ///
 ///     assert_eq!(
 ///         result.readonly().as_array(),
@@ -273,9 +238,19 @@ macro_rules! einsum {
 ///
 /// [einsum]: https://numpy.org/doc/stable/reference/generated/numpy.einsum.html
 #[macro_export]
+macro_rules! einsum {
+    ($subscripts:literal $(,$array:ident)+ $(,)*) => {{
+        let arrays = [$($array.to_dyn().as_borrowed(),)+];
+        $crate::einsum(concat!($subscripts, "\0"), &arrays)
+    }};
+}
+
+/// Deprecated name for [`einsum!`].
+#[deprecated(since = "0.23.0", note = "renamed to `einsum!`")]
+#[macro_export]
 macro_rules! einsum_bound {
     ($subscripts:literal $(,$array:ident)+ $(,)*) => {{
         let arrays = [$($array.to_dyn().as_borrowed(),)+];
-        $crate::einsum_bound(concat!($subscripts, "\0"), &arrays)
+        $crate::einsum(concat!($subscripts, "\0"), &arrays)
     }};
 }
diff --git a/src/untyped_array.rs b/src/untyped_array.rs
index d70dfb5c4..aca2dea5b 100644
--- a/src/untyped_array.rs
+++ b/src/untyped_array.rs
@@ -3,11 +3,8 @@
 //! [ndarray]: https://numpy.org/doc/stable/reference/arrays.ndarray.html
 use std::slice;
 
-#[cfg(feature = "gil-refs")]
-use pyo3::PyNativeType;
 use pyo3::{
-    ffi, pyobject_native_type_extract, pyobject_native_type_named, types::PyAnyMethods,
-    AsPyPointer, Bound, IntoPy, PyAny, PyObject, PyTypeInfo, Python,
+    ffi, pyobject_native_type_named, types::PyAnyMethods, Bound, PyAny, PyTypeInfo, Python,
 };
 
 use crate::array::{PyArray, PyArrayMethods};
@@ -30,7 +27,7 @@ use crate::npyffi;
 /// ```
 /// # use pyo3::prelude::*;
 /// use pyo3::exceptions::PyTypeError;
-/// use numpy::{Element, PyUntypedArray, PyArray1, dtype_bound};
+/// use numpy::{Element, PyUntypedArray, PyArray1, dtype};
 /// use numpy::{PyUntypedArrayMethods, PyArrayMethods, PyArrayDescrMethods};
 ///
 /// #[pyfunction]
@@ -43,11 +40,11 @@ use crate::npyffi;
 ///
 ///     let element_type = array.dtype();
 ///
-///     if element_type.is_equiv_to(&dtype_bound::<f32>(py)) {
+///     if element_type.is_equiv_to(&dtype::<f32>(py)) {
 ///         let array = array.downcast::<PyArray1<f32>>()?;
 ///
 ///         implementation(array)
-///     } else if element_type.is_equiv_to(&dtype_bound::<f64>(py)) {
+///     } else if element_type.is_equiv_to(&dtype::<f64>(py)) {
 ///         let array = array.downcast::<PyArray1<f64>>()?;
 ///
 ///         implementation(array)
@@ -57,7 +54,7 @@ use crate::npyffi;
 /// }
 /// #
 /// # Python::with_gil(|py| {
-/// #   let array = PyArray1::<f64>::zeros_bound(py, 42, false);
+/// #   let array = PyArray1::<f64>::zeros(py, 42, false);
 /// #   entry_point(py, array.as_untyped())
 /// # }).unwrap();
 /// ```
@@ -72,194 +69,13 @@ unsafe impl PyTypeInfo for PyUntypedArray {
         unsafe { npyffi::PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type) }
     }
 
-    fn is_type_of_bound(ob: &Bound<'_, PyAny>) -> bool {
+    fn is_type_of(ob: &Bound<'_, PyAny>) -> bool {
         unsafe { npyffi::PyArray_Check(ob.py(), ob.as_ptr()) != 0 }
     }
 }
 
 pyobject_native_type_named!(PyUntypedArray);
 
-impl IntoPy<PyObject> for PyUntypedArray {
-    fn into_py<'py>(self, py: Python<'py>) -> PyObject {
-        unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
-    }
-}
-
-pyobject_native_type_extract!(PyUntypedArray);
-
-#[cfg(feature = "gil-refs")]
-impl PyUntypedArray {
-    /// Returns a raw pointer to the underlying [`PyArrayObject`][npyffi::PyArrayObject].
-    #[inline]
-    pub fn as_array_ptr(&self) -> *mut npyffi::PyArrayObject {
-        self.as_borrowed().as_array_ptr()
-    }
-
-    /// Returns the `dtype` of the array.
-    ///
-    /// See also [`ndarray.dtype`][ndarray-dtype] and [`PyArray_DTYPE`][PyArray_DTYPE].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::prelude::*;
-    /// use numpy::{dtype_bound, PyArray};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///    let array = PyArray::from_vec_bound(py, vec![1_i32, 2, 3]);
-    ///
-    ///    assert!(array.dtype().is_equiv_to(&dtype_bound::<i32>(py)));
-    /// });
-    /// ```
-    ///
-    /// [ndarray-dtype]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.dtype.html
-    /// [PyArray_DTYPE]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_DTYPE
-    #[inline]
-    pub fn dtype(&self) -> &PyArrayDescr {
-        self.as_borrowed().dtype().into_gil_ref()
-    }
-
-    /// Returns `true` if the internal data of the array is contiguous,
-    /// indepedently of whether C-style/row-major or Fortran-style/column-major.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray1, PyUntypedArrayMethods};
-    /// use pyo3::{types::{IntoPyDict, PyAnyMethods}, Python};
-    ///
-    /// Python::with_gil(|py| {
-    ///     let array = PyArray1::arange_bound(py, 0, 10, 1);
-    ///     assert!(array.is_contiguous());
-    ///
-    ///     let view = py
-    ///         .eval_bound("array[::2]", None, Some(&[("array", array)].into_py_dict_bound(py)))
-    ///         .unwrap()
-    ///         .downcast_into::<PyArray1<i32>>()
-    ///         .unwrap();
-    ///     assert!(!view.is_contiguous());
-    /// });
-    /// ```
-    #[inline]
-    pub fn is_contiguous(&self) -> bool {
-        self.as_borrowed().is_contiguous()
-    }
-
-    /// Returns `true` if the internal data of the array is Fortran-style/column-major contiguous.
-    #[inline]
-    pub fn is_fortran_contiguous(&self) -> bool {
-        self.as_borrowed().is_fortran_contiguous()
-    }
-
-    /// Returns `true` if the internal data of the array is C-style/row-major contiguous.
-    #[inline]
-    pub fn is_c_contiguous(&self) -> bool {
-        self.as_borrowed().is_c_contiguous()
-    }
-
-    /// Returns the number of dimensions of the array.
-    ///
-    /// See also [`ndarray.ndim`][ndarray-ndim] and [`PyArray_NDIM`][PyArray_NDIM].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray3, PyUntypedArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let arr = PyArray3::<f64>::zeros_bound(py, [4, 5, 6], false);
-    ///
-    ///     assert_eq!(arr.ndim(), 3);
-    /// });
-    /// ```
-    ///
-    /// [ndarray-ndim]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.ndim.html
-    /// [PyArray_NDIM]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_NDIM
-    #[inline]
-    pub fn ndim(&self) -> usize {
-        self.as_borrowed().ndim()
-    }
-
-    /// Returns a slice indicating how many bytes to advance when iterating along each axis.
-    ///
-    /// See also [`ndarray.strides`][ndarray-strides] and [`PyArray_STRIDES`][PyArray_STRIDES].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray3, PyUntypedArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let arr = PyArray3::<f64>::zeros_bound(py, [4, 5, 6], false);
-    ///
-    ///     assert_eq!(arr.strides(), &[240, 48, 8]);
-    /// });
-    /// ```
-    /// [ndarray-strides]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html
-    /// [PyArray_STRIDES]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_STRIDES
-    #[inline]
-    pub fn strides(&self) -> &[isize] {
-        let n = self.ndim();
-        if n == 0 {
-            cold();
-            return &[];
-        }
-        let ptr = self.as_array_ptr();
-        unsafe {
-            let p = (*ptr).strides;
-            slice::from_raw_parts(p, n)
-        }
-    }
-
-    /// Returns a slice which contains dimmensions of the array.
-    ///
-    /// See also [`ndarray.shape`][ndaray-shape] and [`PyArray_DIMS`][PyArray_DIMS].
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use numpy::{PyArray3, PyUntypedArrayMethods};
-    /// use pyo3::Python;
-    ///
-    /// Python::with_gil(|py| {
-    ///     let arr = PyArray3::<f64>::zeros_bound(py, [4, 5, 6], false);
-    ///
-    ///     assert_eq!(arr.shape(), &[4, 5, 6]);
-    /// });
-    /// ```
-    ///
-    /// [ndarray-shape]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.shape.html
-    /// [PyArray_DIMS]: https://numpy.org/doc/stable/reference/c-api/array.html#c.PyArray_DIMS
-    #[inline]
-    pub fn shape(&self) -> &[usize] {
-        let n = self.ndim();
-        if n == 0 {
-            cold();
-            return &[];
-        }
-        let ptr = self.as_array_ptr();
-        unsafe {
-            let p = (*ptr).dimensions as *mut usize;
-            slice::from_raw_parts(p, n)
-        }
-    }
-
-    /// Calculates the total number of elements in the array.
-    #[inline]
-    pub fn len(&self) -> usize {
-        self.as_borrowed().len()
-    }
-
-    /// Returns `true` if the there are no elements in the array.
-    #[inline]
-    pub fn is_empty(&self) -> bool {
-        self.as_borrowed().is_empty()
-    }
-}
-
 /// Implementation of functionality for [`PyUntypedArray`].
 #[doc(alias = "PyUntypedArray")]
 pub trait PyUntypedArrayMethods<'py>: Sealed {
@@ -274,13 +90,13 @@ pub trait PyUntypedArrayMethods<'py>: Sealed {
     ///
     /// ```
     /// use numpy::prelude::*;
-    /// use numpy::{dtype_bound, PyArray};
+    /// use numpy::{dtype, PyArray};
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///    let array = PyArray::from_vec_bound(py, vec![1_i32, 2, 3]);
+    ///    let array = PyArray::from_vec(py, vec![1_i32, 2, 3]);
     ///
-    ///    assert!(array.dtype().is_equiv_to(&dtype_bound::<i32>(py)));
+    ///    assert!(array.dtype().is_equiv_to(&dtype::<i32>(py)));
     /// });
     /// ```
     ///
@@ -295,19 +111,20 @@ pub trait PyUntypedArrayMethods<'py>: Sealed {
     ///
     /// ```
     /// use numpy::{PyArray1, PyUntypedArrayMethods};
-    /// use pyo3::{types::{IntoPyDict, PyAnyMethods}, Python};
+    /// use pyo3::{types::{IntoPyDict, PyAnyMethods}, Python, ffi::c_str};
     ///
+    /// # fn main() -> pyo3::PyResult<()> {
     /// Python::with_gil(|py| {
-    ///     let array = PyArray1::arange_bound(py, 0, 10, 1);
+    ///     let array = PyArray1::arange(py, 0, 10, 1);
     ///     assert!(array.is_contiguous());
     ///
     ///     let view = py
-    ///         .eval_bound("array[::2]", None, Some(&[("array", array)].into_py_dict_bound(py)))
-    ///         .unwrap()
-    ///         .downcast_into::<PyArray1<i32>>()
-    ///         .unwrap();
+    ///         .eval(c_str!("array[::2]"), None, Some(&[("array", array)].into_py_dict(py)?))?
+    ///         .downcast_into::<PyArray1<i32>>()?;
     ///     assert!(!view.is_contiguous());
-    /// });
+    /// #   Ok(())
+    /// })
+    /// # }
     /// ```
     fn is_contiguous(&self) -> bool {
         unsafe {
@@ -339,7 +156,7 @@ pub trait PyUntypedArrayMethods<'py>: Sealed {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let arr = PyArray3::<f64>::zeros_bound(py, [4, 5, 6], false);
+    ///     let arr = PyArray3::<f64>::zeros(py, [4, 5, 6], false);
     ///
     ///     assert_eq!(arr.ndim(), 3);
     /// });
@@ -363,7 +180,7 @@ pub trait PyUntypedArrayMethods<'py>: Sealed {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let arr = PyArray3::<f64>::zeros_bound(py, [4, 5, 6], false);
+    ///     let arr = PyArray3::<f64>::zeros(py, [4, 5, 6], false);
     ///
     ///     assert_eq!(arr.strides(), &[240, 48, 8]);
     /// });
@@ -395,7 +212,7 @@ pub trait PyUntypedArrayMethods<'py>: Sealed {
     /// use pyo3::Python;
     ///
     /// Python::with_gil(|py| {
-    ///     let arr = PyArray3::<f64>::zeros_bound(py, [4, 5, 6], false);
+    ///     let arr = PyArray3::<f64>::zeros(py, [4, 5, 6], false);
     ///
     ///     assert_eq!(arr.shape(), &[4, 5, 6]);
     /// });
diff --git a/tests/array.rs b/tests/array.rs
index 856d4af10..af4bee1b2 100644
--- a/tests/array.rs
+++ b/tests/array.rs
@@ -5,24 +5,25 @@ use half::{bf16, f16};
 use ndarray::{array, s, Array1, Dim};
 use numpy::prelude::*;
 use numpy::{
-    dtype_bound, get_array_module, npyffi::NPY_ORDER, pyarray_bound, PyArray, PyArray1, PyArray2,
-    PyArrayDescr, PyFixedString, PyFixedUnicode,
+    dtype, get_array_module, npyffi::NPY_ORDER, pyarray, PyArray, PyArray1, PyArray2, PyArrayDescr,
+    PyFixedString, PyFixedUnicode,
 };
+use pyo3::ffi::c_str;
 use pyo3::{
     py_run, pyclass, pymethods,
     types::{IntoPyDict, PyAnyMethods, PyDict, PyList},
     Bound, Py, Python,
 };
-#[cfg(feature = "gil-refs")]
-use {numpy::PyArrayDyn, pyo3::PyResult};
 
 fn get_np_locals(py: Python<'_>) -> Bound<'_, PyDict> {
-    [("np", get_array_module(py).unwrap())].into_py_dict_bound(py)
+    [("np", get_array_module(py).unwrap())]
+        .into_py_dict(py)
+        .unwrap()
 }
 
 fn not_contiguous_array(py: Python<'_>) -> Bound<'_, PyArray1<i32>> {
-    py.eval_bound(
-        "np.array([1, 2, 3, 4], dtype='int32')[::2]",
+    py.eval(
+        c_str!("np.array([1, 2, 3, 4], dtype='int32')[::2]"),
         None,
         Some(&get_np_locals(py)),
     )
@@ -36,7 +37,7 @@ fn new_c_order() {
     Python::with_gil(|py| {
         let dims = [3, 5];
 
-        let arr = PyArray::<f64, _>::zeros_bound(py, dims, false);
+        let arr = PyArray::<f64, _>::zeros(py, dims, false);
 
         assert!(arr.ndim() == 2);
         assert!(arr.dims() == dims);
@@ -58,7 +59,7 @@ fn new_fortran_order() {
     Python::with_gil(|py| {
         let dims = [3, 5];
 
-        let arr = PyArray::<f64, _>::zeros_bound(py, dims, true);
+        let arr = PyArray::<f64, _>::zeros(py, dims, true);
 
         assert!(arr.ndim() == 2);
         assert!(arr.dims() == dims);
@@ -80,7 +81,7 @@ fn tuple_as_dim() {
     Python::with_gil(|py| {
         let dims = (3, 5);
 
-        let arr = PyArray::<f64, _>::zeros_bound(py, dims, false);
+        let arr = PyArray::<f64, _>::zeros(py, dims, false);
 
         assert!(arr.ndim() == 2);
         assert!(arr.dims() == [3, 5]);
@@ -90,11 +91,11 @@ fn tuple_as_dim() {
 #[test]
 fn rank_zero_array_has_invalid_strides_dimensions() {
     Python::with_gil(|py| {
-        let arr = PyArray::<f64, _>::zeros_bound(py, (), false);
+        let arr = PyArray::<f64, _>::zeros(py, (), false);
 
         assert_eq!(arr.ndim(), 0);
-        assert_eq!(arr.strides(), &[]);
-        assert_eq!(arr.shape(), &[]);
+        assert_eq!(arr.strides(), &[] as &[isize]);
+        assert_eq!(arr.shape(), &[] as &[usize]);
 
         assert_eq!(arr.len(), 1);
         assert!(!arr.is_empty());
@@ -108,7 +109,7 @@ fn zeros() {
     Python::with_gil(|py| {
         let dims = [3, 4];
 
-        let arr = PyArray::<f64, _>::zeros_bound(py, dims, false);
+        let arr = PyArray::<f64, _>::zeros(py, dims, false);
 
         assert!(arr.ndim() == 2);
         assert!(arr.dims() == dims);
@@ -116,7 +117,7 @@ fn zeros() {
         let size = size_of::<f64>() as isize;
         assert!(arr.strides() == [dims[1] as isize * size, size]);
 
-        let arr = PyArray::<f64, _>::zeros_bound(py, dims, true);
+        let arr = PyArray::<f64, _>::zeros(py, dims, true);
 
         assert!(arr.ndim() == 2);
         assert!(arr.dims() == dims);
@@ -129,7 +130,7 @@ fn zeros() {
 #[test]
 fn arange() {
     Python::with_gil(|py| {
-        let arr = PyArray::<f64, _>::arange_bound(py, 0.0, 1.0, 0.1);
+        let arr = PyArray::<f64, _>::arange(py, 0.0, 1.0, 0.1);
 
         assert_eq!(arr.ndim(), 1);
         assert_eq!(arr.dims(), Dim([10]));
@@ -139,7 +140,7 @@ fn arange() {
 #[test]
 fn as_array() {
     Python::with_gil(|py| {
-        let pyarr = PyArray::<f64, _>::zeros_bound(py, [3, 2, 4], false).readonly();
+        let pyarr = PyArray::<f64, _>::zeros(py, [3, 2, 4], false).readonly();
         let arr = pyarr.as_array();
 
         assert_eq!(pyarr.shape(), arr.shape());
@@ -173,7 +174,7 @@ fn as_raw_array() {
 #[test]
 fn as_slice() {
     Python::with_gil(|py| {
-        let arr = PyArray::<i32, _>::zeros_bound(py, [3, 2, 4], false);
+        let arr = PyArray::<i32, _>::zeros(py, [3, 2, 4], false);
         assert_eq!(arr.readonly().as_slice().unwrap().len(), 3 * 2 * 4);
 
         let not_contiguous = not_contiguous_array(py);
@@ -185,7 +186,7 @@ fn as_slice() {
 #[test]
 fn is_instance() {
     Python::with_gil(|py| {
-        let arr = PyArray2::<f64>::zeros_bound(py, [3, 5], false);
+        let arr = PyArray2::<f64>::zeros(py, [3, 5], false);
 
         assert!(arr.is_instance_of::<PyArray2<f64>>());
         assert!(!arr.is_instance_of::<PyList>());
@@ -195,7 +196,7 @@ fn is_instance() {
 #[test]
 fn from_vec2() {
     Python::with_gil(|py| {
-        let pyarray = PyArray::from_vec2_bound(py, &[vec![1, 2, 3], vec![4, 5, 6]]).unwrap();
+        let pyarray = PyArray::from_vec2(py, &[vec![1, 2, 3], vec![4, 5, 6]]).unwrap();
 
         assert_eq!(pyarray.readonly().as_array(), array![[1, 2, 3], [4, 5, 6]]);
     });
@@ -204,7 +205,7 @@ fn from_vec2() {
 #[test]
 fn from_vec2_ragged() {
     Python::with_gil(|py| {
-        let pyarray = PyArray::from_vec2_bound(py, &[vec![1, 2, 3], vec![4, 5]]);
+        let pyarray = PyArray::from_vec2(py, &[vec![1, 2, 3], vec![4, 5]]);
 
         let err = pyarray.unwrap_err();
         assert_eq!(err.to_string(), "invalid length: 2, but expected 3");
@@ -214,7 +215,7 @@ fn from_vec2_ragged() {
 #[test]
 fn from_vec3() {
     Python::with_gil(|py| {
-        let pyarray = PyArray::from_vec3_bound(
+        let pyarray = PyArray::from_vec3(
             py,
             &[
                 vec![vec![1, 2], vec![3, 4]],
@@ -234,7 +235,7 @@ fn from_vec3() {
 #[test]
 fn from_vec3_ragged() {
     Python::with_gil(|py| {
-        let pyarray = PyArray::from_vec3_bound(
+        let pyarray = PyArray::from_vec3(
             py,
             &[
                 vec![vec![1, 2], vec![3, 4]],
@@ -246,7 +247,7 @@ fn from_vec3_ragged() {
         let err = pyarray.unwrap_err();
         assert_eq!(err.to_string(), "invalid length: 1, but expected 2");
 
-        let pyarray = PyArray::from_vec3_bound(
+        let pyarray = PyArray::from_vec3(
             py,
             &[
                 vec![vec![1, 2], vec![3, 4]],
@@ -260,101 +261,10 @@ fn from_vec3_ragged() {
     });
 }
 
-#[test]
-#[cfg(feature = "gil-refs")]
-fn extract_as_fixed() {
-    Python::with_gil(|py| {
-        let locals = get_np_locals(py);
-        let pyarray: &PyArray1<i32> = py
-            .eval_bound("np.array([1, 2, 3], dtype='int32')", Some(&locals), None)
-            .unwrap()
-            .extract()
-            .unwrap();
-
-        assert_eq!(pyarray.readonly().as_array(), array![1, 2, 3]);
-    });
-}
-
-#[test]
-#[cfg(feature = "gil-refs")]
-fn extract_as_dyn() {
-    Python::with_gil(|py| {
-        let locals = get_np_locals(py);
-        let pyarray: &PyArrayDyn<i32> = py
-            .eval_bound(
-                "np.array([[1, 2], [3, 4]], dtype='int32')",
-                Some(&locals),
-                None,
-            )
-            .unwrap()
-            .extract()
-            .unwrap();
-
-        assert_eq!(
-            pyarray.readonly().as_array(),
-            array![[1, 2], [3, 4]].into_dyn()
-        );
-    });
-}
-
-#[test]
-#[cfg(feature = "gil-refs")]
-fn extract_fail_by_check() {
-    Python::with_gil(|py| {
-        let locals = get_np_locals(py);
-        let pyarray: PyResult<&PyArray2<i32>> = py
-            .eval_bound("[1, 2, 3]", Some(&locals), None)
-            .unwrap()
-            .extract();
-
-        let err = pyarray.unwrap_err();
-        assert_eq!(
-            err.to_string(),
-            "TypeError: 'list' object cannot be converted to 'PyArray<T, D>'"
-        );
-    });
-}
-
-#[test]
-#[cfg(feature = "gil-refs")]
-fn extract_fail_by_dim() {
-    Python::with_gil(|py| {
-        let locals = get_np_locals(py);
-        let pyarray: PyResult<&PyArray2<i32>> = py
-            .eval_bound("np.array([1, 2, 3], dtype='int32')", Some(&locals), None)
-            .unwrap()
-            .extract();
-
-        let err = pyarray.unwrap_err();
-        assert_eq!(
-            err.to_string(),
-            "TypeError: dimensionality mismatch:\n from=1, to=2"
-        );
-    });
-}
-
-#[test]
-#[cfg(feature = "gil-refs")]
-fn extract_fail_by_dtype() {
-    Python::with_gil(|py| {
-        let locals = get_np_locals(py);
-        let pyarray: PyResult<&PyArray1<i32>> = py
-            .eval_bound("np.array([1, 2, 3], dtype='float64')", Some(&locals), None)
-            .unwrap()
-            .extract();
-
-        let err = pyarray.unwrap_err();
-        assert_eq!(
-            err.to_string(),
-            "TypeError: type mismatch:\n from=float64, to=int32"
-        );
-    });
-}
-
 #[test]
 fn array_cast() {
     Python::with_gil(|py| {
-        let arr_f64 = pyarray_bound![py, [1.5, 2.5, 3.5], [1.5, 2.5, 3.5]];
+        let arr_f64 = pyarray![py, [1.5, 2.5, 3.5], [1.5, 2.5, 3.5]];
         let arr_i32 = arr_f64.cast::<i32>(false).unwrap();
 
         assert_eq!(arr_i32.readonly().as_array(), array![[1, 2, 3], [1, 2, 3]]);
@@ -365,12 +275,12 @@ fn array_cast() {
 fn handle_negative_strides() {
     Python::with_gil(|py| {
         let arr = array![[2, 3], [4, 5u32]];
-        let pyarr = arr.to_pyarray_bound(py);
+        let pyarr = arr.to_pyarray(py);
 
         let neg_str_pyarr = py
-            .eval_bound(
-                "a[::-1]",
-                Some(&[("a", pyarr)].into_py_dict_bound(py)),
+            .eval(
+                c_str!("a[::-1]"),
+                Some(&[("a", pyarr)].into_py_dict(py).unwrap()),
                 None,
             )
             .unwrap()
@@ -388,19 +298,19 @@ fn handle_negative_strides() {
 fn dtype_via_python_attribute() {
     Python::with_gil(|py| {
         let arr = array![[2, 3], [4, 5u32]];
-        let pyarr = arr.to_pyarray_bound(py);
+        let pyarr = arr.to_pyarray(py);
 
         let dt = py
-            .eval_bound(
-                "a.dtype",
-                Some(&[("a", pyarr)].into_py_dict_bound(py)),
+            .eval(
+                c_str!("a.dtype"),
+                Some(&[("a", pyarr)].into_py_dict(py).unwrap()),
                 None,
             )
             .unwrap()
             .downcast_into::<PyArrayDescr>()
             .unwrap();
 
-        assert!(dt.is_equiv_to(&dtype_bound::<u32>(py)));
+        assert!(dt.is_equiv_to(&dtype::<u32>(py)));
     });
 }
 
@@ -415,7 +325,7 @@ impl Owner {
     fn array(this: Bound<'_, Self>) -> Bound<'_, PyArray1<f64>> {
         let array = &this.borrow().array;
 
-        unsafe { PyArray1::borrow_from_array_bound(array, this.into_any()) }
+        unsafe { PyArray1::borrow_from_array(array, this.into_any()) }
     }
 }
 
@@ -441,7 +351,7 @@ fn borrow_from_array_works() {
 #[test]
 fn downcasting_works() {
     Python::with_gil(|py| {
-        let ob = PyArray::from_slice_bound(py, &[1_i32, 2, 3]).into_any();
+        let ob = PyArray::from_slice(py, &[1_i32, 2, 3]).into_any();
 
         assert!(ob.downcast::<PyArray1<i32>>().is_ok());
     });
@@ -450,7 +360,7 @@ fn downcasting_works() {
 #[test]
 fn downcasting_respects_element_type() {
     Python::with_gil(|py| {
-        let ob = PyArray::from_slice_bound(py, &[1_i32, 2, 3]).into_any();
+        let ob = PyArray::from_slice(py, &[1_i32, 2, 3]).into_any();
 
         assert!(ob.downcast::<PyArray1<f64>>().is_err());
     });
@@ -459,7 +369,7 @@ fn downcasting_respects_element_type() {
 #[test]
 fn downcasting_respects_dimensionality() {
     Python::with_gil(|py| {
-        let ob = PyArray::from_slice_bound(py, &[1_i32, 2, 3]).into_any();
+        let ob = PyArray::from_slice(py, &[1_i32, 2, 3]).into_any();
 
         assert!(ob.downcast::<PyArray2<i32>>().is_err());
     });
@@ -468,7 +378,7 @@ fn downcasting_respects_dimensionality() {
 #[test]
 fn unbind_works() {
     let arr: Py<PyArray1<_>> = Python::with_gil(|py| {
-        let arr = PyArray::from_slice_bound(py, &[1_i32, 2, 3]);
+        let arr = PyArray::from_slice(py, &[1_i32, 2, 3]);
 
         arr.unbind()
     });
@@ -480,28 +390,11 @@ fn unbind_works() {
     });
 }
 
-#[test]
-#[cfg(feature = "gil-refs")]
-fn to_owned_works() {
-    let arr: Py<PyArray1<_>> = Python::with_gil(|py| {
-        let arr = PyArray::from_slice_bound(py, &[1_i32, 2, 3]);
-
-        #[allow(deprecated)]
-        arr.as_gil_ref().to_owned()
-    });
-
-    Python::with_gil(|py| {
-        let arr = arr.bind(py);
-
-        assert_eq!(arr.readonly().as_slice().unwrap(), &[1, 2, 3]);
-    });
-}
-
 #[test]
 fn copy_to_works() {
     Python::with_gil(|py| {
-        let arr1 = PyArray::arange_bound(py, 2.0, 5.0, 1.0);
-        let arr2 = unsafe { PyArray::<i64, _>::new_bound(py, [3], false) };
+        let arr1 = PyArray::arange(py, 2.0, 5.0, 1.0);
+        let arr2 = unsafe { PyArray::<i64, _>::new(py, [3], false) };
 
         arr1.copy_to(&arr2).unwrap();
 
@@ -512,7 +405,7 @@ fn copy_to_works() {
 #[test]
 fn get_works() {
     Python::with_gil(|py| {
-        let array = pyarray_bound![py, [[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]];
+        let array = pyarray![py, [[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]];
 
         unsafe {
             assert_eq!(array.get([0, 0, 0]), Some(&1));
@@ -531,7 +424,7 @@ fn get_works() {
 #[test]
 fn permute_and_transpose() {
     Python::with_gil(|py| {
-        let array = array![[0, 1, 2], [3, 4, 5]].into_pyarray_bound(py);
+        let array = array![[0, 1, 2], [3, 4, 5]].into_pyarray(py);
 
         let permuted = array.permute(Some([1, 0])).unwrap();
         assert_eq!(
@@ -551,7 +444,7 @@ fn permute_and_transpose() {
             array![[0, 3], [1, 4], [2, 5]]
         );
 
-        let array = pyarray_bound![py, [[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]];
+        let array = pyarray![py, [[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]];
 
         let permuted = array.permute(Some([0, 2, 1])).unwrap();
         assert_eq!(
@@ -564,7 +457,7 @@ fn permute_and_transpose() {
 #[test]
 fn reshape() {
     Python::with_gil(|py| {
-        let array = PyArray::from_iter_bound(py, 0..9)
+        let array = PyArray::from_iter(py, 0..9)
             .reshape_with_order([3, 3], NPY_ORDER::NPY_FORTRANORDER)
             .unwrap();
 
@@ -582,12 +475,12 @@ fn reshape() {
 #[test]
 fn half_f16_works() {
     Python::with_gil(|py| {
-        let np = py.eval_bound("__import__('numpy')", None, None).unwrap();
-        let locals = [("np", &np)].into_py_dict_bound(py);
+        let np = py.eval(c_str!("__import__('numpy')"), None, None).unwrap();
+        let locals = [("np", &np)].into_py_dict(py).unwrap();
 
         let array = py
-            .eval_bound(
-                "np.array([[1, 2], [3, 4]], dtype='float16')",
+            .eval(
+                c_str!("np.array([[1, 2], [3, 4]], dtype='float16')"),
                 None,
                 Some(&locals),
             )
@@ -620,17 +513,17 @@ fn half_f16_works() {
 #[test]
 fn half_bf16_works() {
     Python::with_gil(|py| {
-        let np = py.eval_bound("__import__('numpy')", None, None).unwrap();
+        let np = py.eval(c_str!("__import__('numpy')"), None, None).unwrap();
         // NumPy itself does not provide a `bfloat16` dtype itself,
         // so we import ml_dtypes which does register such a dtype.
         let mldt = py
-            .eval_bound("__import__('ml_dtypes')", None, None)
+            .eval(c_str!("__import__('ml_dtypes')"), None, None)
             .unwrap();
-        let locals = [("np", &np), ("mldt", &mldt)].into_py_dict_bound(py);
+        let locals = [("np", &np), ("mldt", &mldt)].into_py_dict(py).unwrap();
 
         let array = py
-            .eval_bound(
-                "np.array([[1, 2], [3, 4]], dtype='bfloat16')",
+            .eval(
+                c_str!("np.array([[1, 2], [3, 4]], dtype='bfloat16')"),
                 None,
                 Some(&locals),
             )
@@ -662,12 +555,12 @@ fn half_bf16_works() {
 #[test]
 fn ascii_strings_with_explicit_dtype_works() {
     Python::with_gil(|py| {
-        let np = py.eval_bound("__import__('numpy')", None, None).unwrap();
-        let locals = [("np", &np)].into_py_dict_bound(py);
+        let np = py.eval(c_str!("__import__('numpy')"), None, None).unwrap();
+        let locals = [("np", &np)].into_py_dict(py).unwrap();
 
         let array = py
-            .eval_bound(
-                "np.array([b'foo', b'bar', b'foobar'], dtype='S6')",
+            .eval(
+                c_str!("np.array([b'foo', b'bar', b'foobar'], dtype='S6')"),
                 None,
                 Some(&locals),
             )
@@ -698,12 +591,12 @@ fn ascii_strings_with_explicit_dtype_works() {
 #[test]
 fn unicode_strings_with_explicit_dtype_works() {
     Python::with_gil(|py| {
-        let np = py.eval_bound("__import__('numpy')", None, None).unwrap();
-        let locals = [("np", &np)].into_py_dict_bound(py);
+        let np = py.eval(c_str!("__import__('numpy')"), None, None).unwrap();
+        let locals = [("np", &np)].into_py_dict(py).unwrap();
 
         let array = py
-            .eval_bound(
-                "np.array(['foo', 'bar', 'foobar'], dtype='U6')",
+            .eval(
+                c_str!("np.array(['foo', 'bar', 'foobar'], dtype='U6')"),
                 None,
                 Some(&locals),
             )
@@ -719,7 +612,14 @@ fn unicode_strings_with_explicit_dtype_works() {
             assert_eq!(array[1].0, [b'b' as _, b'a' as _, b'r' as _, 0, 0, 0]);
             assert_eq!(
                 array[2].0,
-                [b'f' as _, b'o' as _, b'o' as _, b'b' as _, b'a' as _, b'r' as _]
+                [
+                    b'f' as u32,
+                    b'o' as _,
+                    b'o' as _,
+                    b'b' as _,
+                    b'a' as _,
+                    b'r' as _
+                ]
             );
         }
 
@@ -737,12 +637,12 @@ fn unicode_strings_with_explicit_dtype_works() {
 #[test]
 fn ascii_strings_ignore_byteorder() {
     Python::with_gil(|py| {
-        let np = py.eval_bound("__import__('numpy')", None, None).unwrap();
-        let locals = [("np", &np)].into_py_dict_bound(py);
+        let np = py.eval(c_str!("__import__('numpy')"), None, None).unwrap();
+        let locals = [("np", &np)].into_py_dict(py).unwrap();
 
         let native_endian_works = py
-            .eval_bound(
-                "np.array([b'foo', b'bar'], dtype='=S3')",
+            .eval(
+                c_str!("np.array([b'foo', b'bar'], dtype='=S3')"),
                 None,
                 Some(&locals),
             )
@@ -751,8 +651,8 @@ fn ascii_strings_ignore_byteorder() {
             .is_ok();
 
         let little_endian_works = py
-            .eval_bound(
-                "np.array(['bfoo', b'bar'], dtype='<S3')",
+            .eval(
+                c_str!("np.array(['bfoo', b'bar'], dtype='<S3')"),
                 None,
                 Some(&locals),
             )
@@ -761,8 +661,8 @@ fn ascii_strings_ignore_byteorder() {
             .is_ok();
 
         let big_endian_works = py
-            .eval_bound(
-                "np.array([b'foo', b'bar'], dtype='>S3')",
+            .eval(
+                c_str!("np.array([b'foo', b'bar'], dtype='>S3')"),
                 None,
                 Some(&locals),
             )
@@ -780,23 +680,35 @@ fn ascii_strings_ignore_byteorder() {
 #[test]
 fn unicode_strings_respect_byteorder() {
     Python::with_gil(|py| {
-        let np = py.eval_bound("__import__('numpy')", None, None).unwrap();
-        let locals = [("np", &np)].into_py_dict_bound(py);
+        let np = py.eval(c_str!("__import__('numpy')"), None, None).unwrap();
+        let locals = [("np", &np)].into_py_dict(py).unwrap();
 
         let native_endian_works = py
-            .eval_bound("np.array(['foo', 'bar'], dtype='=U3')", None, Some(&locals))
+            .eval(
+                c_str!("np.array(['foo', 'bar'], dtype='=U3')"),
+                None,
+                Some(&locals),
+            )
             .unwrap()
             .downcast::<PyArray1<PyFixedUnicode<3>>>()
             .is_ok();
 
         let little_endian_works = py
-            .eval_bound("np.array(['foo', 'bar'], dtype='<U3')", None, Some(&locals))
+            .eval(
+                c_str!("np.array(['foo', 'bar'], dtype='<U3')"),
+                None,
+                Some(&locals),
+            )
             .unwrap()
             .downcast::<PyArray1<PyFixedUnicode<3>>>()
             .is_ok();
 
         let big_endian_works = py
-            .eval_bound("np.array(['foo', 'bar'], dtype='>U3')", None, Some(&locals))
+            .eval(
+                c_str!("np.array(['foo', 'bar'], dtype='>U3')"),
+                None,
+                Some(&locals),
+            )
             .unwrap()
             .downcast::<PyArray1<PyFixedUnicode<3>>>()
             .is_ok();
diff --git a/tests/array_like.rs b/tests/array_like.rs
index cd96f63f5..aa185b174 100644
--- a/tests/array_like.rs
+++ b/tests/array_like.rs
@@ -1,12 +1,15 @@
 use ndarray::array;
 use numpy::{get_array_module, AllowTypeChange, PyArrayLike1, PyArrayLike2, PyArrayLikeDyn};
 use pyo3::{
+    ffi::c_str,
     types::{IntoPyDict, PyAnyMethods, PyDict},
     Bound, Python,
 };
 
 fn get_np_locals(py: Python<'_>) -> Bound<'_, PyDict> {
-    [("np", get_array_module(py).unwrap())].into_py_dict_bound(py)
+    [("np", get_array_module(py).unwrap())]
+        .into_py_dict(py)
+        .unwrap()
 }
 
 #[test]
@@ -14,8 +17,8 @@ fn extract_reference() {
     Python::with_gil(|py| {
         let locals = get_np_locals(py);
         let py_array = py
-            .eval_bound(
-                "np.array([[1,2],[3,4]], dtype='float64')",
+            .eval(
+                c_str!("np.array([[1,2],[3,4]], dtype='float64')"),
                 Some(&locals),
                 None,
             )
@@ -34,8 +37,8 @@ fn convert_array_on_extract() {
     Python::with_gil(|py| {
         let locals = get_np_locals(py);
         let py_array = py
-            .eval_bound(
-                "np.array([[1,2],[3,4]], dtype='int32')",
+            .eval(
+                c_str!("np.array([[1,2],[3,4]], dtype='int32')"),
                 Some(&locals),
                 None,
             )
@@ -54,7 +57,9 @@ fn convert_array_on_extract() {
 #[test]
 fn convert_list_on_extract() {
     Python::with_gil(|py| {
-        let py_list = py.eval_bound("[[1.0,2.0],[3.0,4.0]]", None, None).unwrap();
+        let py_list = py
+            .eval(c_str!("[[1.0,2.0],[3.0,4.0]]"), None, None)
+            .unwrap();
         let extracted_array = py_list.extract::<PyArrayLike2<'_, f64>>().unwrap();
 
         assert_eq!(array![[1.0, 2.0], [3.0, 4.0]], extracted_array.as_array());
@@ -66,7 +71,11 @@ fn convert_array_in_list_on_extract() {
     Python::with_gil(|py| {
         let locals = get_np_locals(py);
         let py_array = py
-            .eval_bound("[np.array([1.0, 2.0]), [3.0, 4.0]]", Some(&locals), None)
+            .eval(
+                c_str!("[np.array([1.0, 2.0]), [3.0, 4.0]]"),
+                Some(&locals),
+                None,
+            )
             .unwrap();
         let extracted_array = py_array.extract::<PyArrayLike2<'_, f64>>().unwrap();
 
@@ -78,7 +87,7 @@ fn convert_array_in_list_on_extract() {
 fn convert_list_on_extract_dyn() {
     Python::with_gil(|py| {
         let py_list = py
-            .eval_bound("[[[1,2],[3,4]],[[5,6],[7,8]]]", None, None)
+            .eval(c_str!("[[[1,2],[3,4]],[[5,6],[7,8]]]"), None, None)
             .unwrap();
         let extracted_array = py_list
             .extract::<PyArrayLikeDyn<'_, i64, AllowTypeChange>>()
@@ -94,7 +103,7 @@ fn convert_list_on_extract_dyn() {
 #[test]
 fn convert_1d_list_on_extract() {
     Python::with_gil(|py| {
-        let py_list = py.eval_bound("[1,2,3,4]", None, None).unwrap();
+        let py_list = py.eval(c_str!("[1,2,3,4]"), None, None).unwrap();
         let extracted_array_1d = py_list.extract::<PyArrayLike1<'_, u32>>().unwrap();
         let extracted_array_dyn = py_list.extract::<PyArrayLikeDyn<'_, f64>>().unwrap();
 
@@ -111,8 +120,8 @@ fn unsafe_cast_shall_fail() {
     Python::with_gil(|py| {
         let locals = get_np_locals(py);
         let py_list = py
-            .eval_bound(
-                "np.array([1.1,2.2,3.3,4.4], dtype='float64')",
+            .eval(
+                c_str!("np.array([1.1,2.2,3.3,4.4], dtype='float64')"),
                 Some(&locals),
                 None,
             )
@@ -128,8 +137,8 @@ fn unsafe_cast_with_coerce_works() {
     Python::with_gil(|py| {
         let locals = get_np_locals(py);
         let py_list = py
-            .eval_bound(
-                "np.array([1.1,2.2,3.3,4.4], dtype='float64')",
+            .eval(
+                c_str!("np.array([1.1,2.2,3.3,4.4], dtype='float64')"),
                 Some(&locals),
                 None,
             )
diff --git a/tests/borrow.rs b/tests/borrow.rs
index 356046c65..2cf38bb84 100644
--- a/tests/borrow.rs
+++ b/tests/borrow.rs
@@ -5,6 +5,7 @@ use numpy::{
     PyReadonlyArray3, PyReadwriteArray3, PyUntypedArrayMethods,
 };
 use pyo3::{
+    ffi::c_str,
     py_run, pyclass, pymethods,
     types::{IntoPyDict, PyAnyMethods},
     Py, Python,
@@ -13,8 +14,8 @@ use pyo3::{
 #[test]
 fn distinct_borrows() {
     Python::with_gil(|py| {
-        let array1 = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
-        let array2 = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array1 = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
+        let array2 = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         let exclusive1 = array1.readwrite();
         let exclusive2 = array2.readwrite();
@@ -27,7 +28,7 @@ fn distinct_borrows() {
 #[test]
 fn multiple_shared_borrows() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         let shared1 = array.readonly();
         let shared2 = array.readonly();
@@ -41,7 +42,7 @@ fn multiple_shared_borrows() {
 #[should_panic(expected = "AlreadyBorrowed")]
 fn exclusive_and_shared_borrows() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         let _exclusive = array.readwrite();
         let _shared = array.readonly();
@@ -52,7 +53,7 @@ fn exclusive_and_shared_borrows() {
 #[should_panic(expected = "AlreadyBorrowed")]
 fn shared_and_exclusive_borrows() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         let _shared = array.readonly();
         let _exclusive = array.readwrite();
@@ -62,7 +63,7 @@ fn shared_and_exclusive_borrows() {
 #[test]
 fn multiple_exclusive_borrows() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         let _exclusive = array.try_readwrite().unwrap();
 
@@ -74,7 +75,7 @@ fn multiple_exclusive_borrows() {
 #[test]
 fn exclusive_borrow_requires_writeable() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         unsafe {
             (*array.as_array_ptr()).flags &= !NPY_ARRAY_WRITEABLE;
@@ -101,7 +102,7 @@ fn borrows_span_frames() {
     Python::with_gil(|py| {
         let borrower = Py::new(py, Borrower).unwrap();
 
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         let _exclusive = array.readwrite();
 
@@ -112,7 +113,7 @@ fn borrows_span_frames() {
 #[test]
 fn borrows_span_threads() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         let _exclusive = array.readwrite();
 
@@ -135,7 +136,7 @@ fn borrows_span_threads() {
 #[test]
 fn shared_borrows_can_be_cloned() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
 
         let shared1 = array.readonly();
         let shared2 = shared1.clone();
@@ -149,18 +150,18 @@ fn shared_borrows_can_be_cloned() {
 #[should_panic(expected = "AlreadyBorrowed")]
 fn overlapping_views_conflict() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
-        let locals = [("array", array)].into_py_dict_bound(py);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
+        let locals = [("array", array)].into_py_dict(py).unwrap();
 
         let view1 = py
-            .eval_bound("array[0,0,0:2]", None, Some(&locals))
+            .eval(c_str!("array[0,0,0:2]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray1<f64>>()
             .unwrap();
         assert_eq!(view1.shape(), [2]);
 
         let view2 = py
-            .eval_bound("array[0,0,1:3]", None, Some(&locals))
+            .eval(c_str!("array[0,0,1:3]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray1<f64>>()
             .unwrap();
@@ -174,18 +175,18 @@ fn overlapping_views_conflict() {
 #[test]
 fn non_overlapping_views_do_not_conflict() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
-        let locals = [("array", array)].into_py_dict_bound(py);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
+        let locals = [("array", array)].into_py_dict(py).unwrap();
 
         let view1 = py
-            .eval_bound("array[0,0,0:1]", None, Some(&locals))
+            .eval(c_str!("array[0,0,0:1]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray1<f64>>()
             .unwrap();
         assert_eq!(view1.shape(), [1]);
 
         let view2 = py
-            .eval_bound("array[0,0,2:3]", None, Some(&locals))
+            .eval(c_str!("array[0,0,2:3]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray1<f64>>()
             .unwrap();
@@ -203,18 +204,18 @@ fn non_overlapping_views_do_not_conflict() {
 #[should_panic(expected = "AlreadyBorrowed")]
 fn conflict_due_to_overlapping_views() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, 3, false);
-        let locals = [("array", array)].into_py_dict_bound(py);
+        let array = PyArray::<f64, _>::zeros(py, 3, false);
+        let locals = [("array", array)].into_py_dict(py).unwrap();
 
         let view1 = py
-            .eval_bound("array[0:2]", None, Some(&locals))
+            .eval(c_str!("array[0:2]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray1<f64>>()
             .unwrap();
         assert_eq!(view1.shape(), [2]);
 
         let view2 = py
-            .eval_bound("array[1:3]", None, Some(&locals))
+            .eval(c_str!("array[1:3]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray1<f64>>()
             .unwrap();
@@ -229,18 +230,18 @@ fn conflict_due_to_overlapping_views() {
 #[should_panic(expected = "AlreadyBorrowed")]
 fn conflict_due_to_reborrow_of_overlapping_views() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, 3, false);
-        let locals = [("array", array)].into_py_dict_bound(py);
+        let array = PyArray::<f64, _>::zeros(py, 3, false);
+        let locals = [("array", array)].into_py_dict(py).unwrap();
 
         let view1 = py
-            .eval_bound("array[0:2]", None, Some(&locals))
+            .eval(c_str!("array[0:2]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray1<f64>>()
             .unwrap();
         assert_eq!(view1.shape(), [2]);
 
         let view2 = py
-            .eval_bound("array[1:3]", None, Some(&locals))
+            .eval(c_str!("array[1:3]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray1<f64>>()
             .unwrap();
@@ -257,25 +258,25 @@ fn conflict_due_to_reborrow_of_overlapping_views() {
 #[test]
 fn interleaved_views_do_not_conflict() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (23, 42, 3), false);
-        let locals = [("array", array)].into_py_dict_bound(py);
+        let array = PyArray::<f64, _>::zeros(py, (23, 42, 3), false);
+        let locals = [("array", array)].into_py_dict(py).unwrap();
 
         let view1 = py
-            .eval_bound("array[:,:,0]", None, Some(&locals))
+            .eval(c_str!("array[:,:,0]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray2<f64>>()
             .unwrap();
         assert_eq!(view1.shape(), [23, 42]);
 
         let view2 = py
-            .eval_bound("array[:,:,1]", None, Some(&locals))
+            .eval(c_str!("array[:,:,1]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray2<f64>>()
             .unwrap();
         assert_eq!(view2.shape(), [23, 42]);
 
         let view3 = py
-            .eval_bound("array[:,:,2]", None, Some(&locals))
+            .eval(c_str!("array[:,:,2]"), None, Some(&locals))
             .unwrap()
             .downcast_into::<PyArray2<f64>>()
             .unwrap();
@@ -294,7 +295,7 @@ fn interleaved_views_do_not_conflict() {
 #[test]
 fn extract_readonly() {
     Python::with_gil(|py| {
-        let ob = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false).into_any();
+        let ob = PyArray::<f64, _>::zeros(py, (1, 2, 3), false).into_any();
         ob.extract::<PyReadonlyArray3<'_, f64>>().unwrap();
     });
 }
@@ -302,7 +303,7 @@ fn extract_readonly() {
 #[test]
 fn extract_readwrite() {
     Python::with_gil(|py| {
-        let ob = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false).into_any();
+        let ob = PyArray::<f64, _>::zeros(py, (1, 2, 3), false).into_any();
         ob.extract::<PyReadwriteArray3<'_, f64>>().unwrap();
     });
 }
@@ -310,7 +311,7 @@ fn extract_readwrite() {
 #[test]
 fn readonly_as_array_slice_get() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
         let array = array.readonly();
 
         assert_eq!(array.as_array().shape(), [1, 2, 3]);
@@ -322,7 +323,7 @@ fn readonly_as_array_slice_get() {
 #[test]
 fn readwrite_as_array_slice() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, (1, 2, 3), false);
+        let array = PyArray::<f64, _>::zeros(py, (1, 2, 3), false);
         let mut array = array.readwrite();
 
         assert_eq!(array.as_array().shape(), [1, 2, 3]);
@@ -337,7 +338,7 @@ fn readwrite_as_array_slice() {
 #[test]
 fn resize_using_exclusive_borrow() {
     Python::with_gil(|py| {
-        let array = PyArray::<f64, _>::zeros_bound(py, 3, false);
+        let array = PyArray::<f64, _>::zeros(py, 3, false);
         assert_eq!(array.shape(), [3]);
 
         let mut array = array.readwrite();
@@ -351,11 +352,15 @@ fn resize_using_exclusive_borrow() {
 #[test]
 fn can_make_python_array_nonwriteable() {
     Python::with_gil(|py| {
-        let array = PyArray1::<f64>::zeros_bound(py, 10, false);
-        let locals = [("array", &array)].into_py_dict_bound(py);
+        let array = PyArray1::<f64>::zeros(py, 10, false);
+        let locals = [("array", &array)].into_py_dict(py).unwrap();
         array.readwrite().make_nonwriteable();
         assert!(!py
-            .eval_bound("array.flags.writeable", None, Some(&locals))
+            .eval(
+                pyo3::ffi::c_str!("array.flags.writeable"),
+                None,
+                Some(&locals)
+            )
             .unwrap()
             .extract::<bool>()
             .unwrap())
@@ -366,7 +371,7 @@ fn can_make_python_array_nonwriteable() {
 #[test]
 fn matrix_from_numpy() {
     Python::with_gil(|py| {
-        let array = numpy::pyarray_bound![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]];
+        let array = numpy::pyarray![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]];
 
         {
             let array = array.readonly();
@@ -404,7 +409,7 @@ fn matrix_from_numpy() {
     });
 
     Python::with_gil(|py| {
-        let array = numpy::pyarray_bound![py, 0, 1, 2];
+        let array = numpy::pyarray![py, 0, 1, 2];
 
         {
             let array = array.readonly();
@@ -430,7 +435,7 @@ fn matrix_from_numpy() {
     });
 
     Python::with_gil(|py| {
-        let array = PyArray::<i32, _>::zeros_bound(py, (2, 2, 2), false);
+        let array = PyArray::<i32, _>::zeros(py, (2, 2, 2), false);
         let array = array.readonly();
 
         let matrix: Option<nalgebra::DMatrixView<'_, i32, nalgebra::Dyn, nalgebra::Dyn>> =
@@ -439,11 +444,11 @@ fn matrix_from_numpy() {
     });
 
     Python::with_gil(|py| {
-        let array = numpy::pyarray_bound![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]];
+        let array = numpy::pyarray![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]];
         let array = py
-            .eval_bound(
-                "a[::-1]",
-                Some(&[("a", array)].into_py_dict_bound(py)),
+            .eval(
+                c_str!("a[::-1]"),
+                Some(&[("a", array)].into_py_dict(py).unwrap()),
                 None,
             )
             .unwrap()
@@ -457,11 +462,11 @@ fn matrix_from_numpy() {
     });
 
     Python::with_gil(|py| {
-        let array = numpy::pyarray_bound![py, [[0, 1], [2, 3]], [[4, 5], [6, 7]]];
+        let array = numpy::pyarray![py, [[0, 1], [2, 3]], [[4, 5], [6, 7]]];
         let array = py
-            .eval_bound(
-                "a[:,:,0]",
-                Some(&[("a", &array)].into_py_dict_bound(py)),
+            .eval(
+                c_str!("a[:,:,0]"),
+                Some(&[("a", &array)].into_py_dict(py).unwrap()),
                 None,
             )
             .unwrap()
@@ -481,11 +486,11 @@ fn matrix_from_numpy() {
     });
 
     Python::with_gil(|py| {
-        let array = numpy::pyarray_bound![py, [[0, 1], [2, 3]], [[4, 5], [6, 7]]];
+        let array = numpy::pyarray![py, [[0, 1], [2, 3]], [[4, 5], [6, 7]]];
         let array = py
-            .eval_bound(
-                "a[:,:,0]",
-                Some(&[("a", &array)].into_py_dict_bound(py)),
+            .eval(
+                c_str!("a[:,:,0]"),
+                Some(&[("a", &array)].into_py_dict(py).unwrap()),
                 None,
             )
             .unwrap()
@@ -505,7 +510,7 @@ fn matrix_from_numpy() {
     });
 
     Python::with_gil(|py| {
-        let array = numpy::pyarray_bound![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]];
+        let array = numpy::pyarray![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]];
         let array = array.readonly();
 
         let matrix: Option<
diff --git a/tests/sum_products.rs b/tests/sum_products.rs
index c41076127..05dfca14a 100644
--- a/tests/sum_products.rs
+++ b/tests/sum_products.rs
@@ -1,31 +1,29 @@
 use numpy::prelude::*;
-use numpy::{
-    array, dot_bound, einsum_bound, inner_bound, pyarray_bound, PyArray0, PyArray1, PyArray2,
-};
+use numpy::{array, dot, einsum, inner, pyarray, PyArray0, PyArray1, PyArray2};
 use pyo3::{Bound, Python};
 
 #[test]
 fn test_dot() {
     Python::with_gil(|py| {
-        let a = pyarray_bound![py, [1, 0], [0, 1]];
-        let b = pyarray_bound![py, [4, 1], [2, 2]];
-        let c: Bound<'_, PyArray2<_>> = dot_bound(&a, &b).unwrap();
+        let a = pyarray![py, [1, 0], [0, 1]];
+        let b = pyarray![py, [4, 1], [2, 2]];
+        let c: Bound<'_, PyArray2<_>> = dot(&a, &b).unwrap();
         assert_eq!(c.readonly().as_array(), array![[4, 1], [2, 2]]);
 
-        let a = pyarray_bound![py, 1, 2, 3];
-        let err = dot_bound::<_, _, _, Bound<'_, PyArray2<_>>>(&a, &b).unwrap_err();
+        let a = pyarray![py, 1, 2, 3];
+        let err = dot::<_, _, _, Bound<'_, PyArray2<_>>>(&a, &b).unwrap_err();
         assert!(err.to_string().contains("not aligned"), "{}", err);
 
-        let a = pyarray_bound![py, 1, 2, 3];
-        let b = pyarray_bound![py, 0, 1, 0];
-        let c: Bound<'_, PyArray0<_>> = dot_bound(&a, &b).unwrap();
+        let a = pyarray![py, 1, 2, 3];
+        let b = pyarray![py, 0, 1, 0];
+        let c: Bound<'_, PyArray0<_>> = dot(&a, &b).unwrap();
         assert_eq!(c.item(), 2);
-        let c: i32 = dot_bound(&a, &b).unwrap();
+        let c: i32 = dot(&a, &b).unwrap();
         assert_eq!(c, 2);
 
-        let a = pyarray_bound![py, 1.0, 2.0, 3.0];
-        let b = pyarray_bound![py, 0.0, 0.0, 0.0];
-        let c: f64 = dot_bound(&a, &b).unwrap();
+        let a = pyarray![py, 1.0, 2.0, 3.0];
+        let b = pyarray![py, 0.0, 0.0, 0.0];
+        let c: f64 = dot(&a, &b).unwrap();
         assert_eq!(c, 0.0);
     });
 }
@@ -33,25 +31,25 @@ fn test_dot() {
 #[test]
 fn test_inner() {
     Python::with_gil(|py| {
-        let a = pyarray_bound![py, 1, 2, 3];
-        let b = pyarray_bound![py, 0, 1, 0];
-        let c: Bound<'_, PyArray0<_>> = inner_bound(&a, &b).unwrap();
+        let a = pyarray![py, 1, 2, 3];
+        let b = pyarray![py, 0, 1, 0];
+        let c: Bound<'_, PyArray0<_>> = inner(&a, &b).unwrap();
         assert_eq!(c.item(), 2);
-        let c: i32 = inner_bound(&a, &b).unwrap();
+        let c: i32 = inner(&a, &b).unwrap();
         assert_eq!(c, 2);
 
-        let a = pyarray_bound![py, 1.0, 2.0, 3.0];
-        let b = pyarray_bound![py, 0.0, 0.0, 0.0];
-        let c: f64 = inner_bound(&a, &b).unwrap();
+        let a = pyarray![py, 1.0, 2.0, 3.0];
+        let b = pyarray![py, 0.0, 0.0, 0.0];
+        let c: f64 = inner(&a, &b).unwrap();
         assert_eq!(c, 0.0);
 
-        let a = pyarray_bound![py, [1, 0], [0, 1]];
-        let b = pyarray_bound![py, [4, 1], [2, 2]];
-        let c: Bound<'_, PyArray2<_>> = inner_bound(&a, &b).unwrap();
+        let a = pyarray![py, [1, 0], [0, 1]];
+        let b = pyarray![py, [4, 1], [2, 2]];
+        let c: Bound<'_, PyArray2<_>> = inner(&a, &b).unwrap();
         assert_eq!(c.readonly().as_array(), array![[4, 2], [1, 2]]);
 
-        let a = pyarray_bound![py, 1, 2, 3];
-        let err = inner_bound::<_, _, _, Bound<'_, PyArray2<_>>>(&a, &b).unwrap_err();
+        let a = pyarray![py, 1, 2, 3];
+        let err = inner::<_, _, _, Bound<'_, PyArray2<_>>>(&a, &b).unwrap_err();
         assert!(err.to_string().contains("not aligned"), "{}", err);
     });
 }
@@ -59,28 +57,28 @@ fn test_inner() {
 #[test]
 fn test_einsum() {
     Python::with_gil(|py| {
-        let a = PyArray1::<i32>::arange_bound(py, 0, 25, 1)
+        let a = PyArray1::<i32>::arange(py, 0, 25, 1)
             .reshape([5, 5])
             .unwrap();
-        let b = pyarray_bound![py, 0, 1, 2, 3, 4];
-        let c = pyarray_bound![py, [0, 1, 2], [3, 4, 5]];
+        let b = pyarray![py, 0, 1, 2, 3, 4];
+        let c = pyarray![py, [0, 1, 2], [3, 4, 5]];
 
-        let d: Bound<'_, PyArray0<_>> = einsum_bound!("ii", a).unwrap();
+        let d: Bound<'_, PyArray0<_>> = einsum!("ii", a).unwrap();
         assert_eq!(d.item(), 60);
 
-        let d: i32 = einsum_bound!("ii", a).unwrap();
+        let d: i32 = einsum!("ii", a).unwrap();
         assert_eq!(d, 60);
 
-        let d: Bound<'_, PyArray1<_>> = einsum_bound!("ii->i", a).unwrap();
+        let d: Bound<'_, PyArray1<_>> = einsum!("ii->i", a).unwrap();
         assert_eq!(d.readonly().as_array(), array![0, 6, 12, 18, 24]);
 
-        let d: Bound<'_, PyArray1<_>> = einsum_bound!("ij->i", a).unwrap();
+        let d: Bound<'_, PyArray1<_>> = einsum!("ij->i", a).unwrap();
         assert_eq!(d.readonly().as_array(), array![10, 35, 60, 85, 110]);
 
-        let d: Bound<'_, PyArray2<_>> = einsum_bound!("ji", c).unwrap();
+        let d: Bound<'_, PyArray2<_>> = einsum!("ji", c).unwrap();
         assert_eq!(d.readonly().as_array(), array![[0, 3], [1, 4], [2, 5]]);
 
-        let d: Bound<'_, PyArray1<_>> = einsum_bound!("ij,j", a, b).unwrap();
+        let d: Bound<'_, PyArray1<_>> = einsum!("ij,j", a, b).unwrap();
         assert_eq!(d.readonly().as_array(), array![30, 80, 130, 180, 230]);
     });
 }
diff --git a/tests/to_py.rs b/tests/to_py.rs
index a68ae38e0..c18d2d6db 100644
--- a/tests/to_py.rs
+++ b/tests/to_py.rs
@@ -6,14 +6,14 @@ use numpy::{prelude::*, PyArray};
 use pyo3::{
     py_run,
     types::{PyAnyMethods, PyDict, PyString},
-    Python, ToPyObject,
+    Python,
 };
 
 #[test]
 fn to_pyarray_vec() {
     Python::with_gil(|py| {
         #[allow(clippy::useless_vec)]
-        let arr = vec![1, 2, 3].to_pyarray_bound(py);
+        let arr = vec![1, 2, 3].to_pyarray(py);
 
         assert_eq!(arr.shape(), [3]);
         assert_eq!(arr.readonly().as_slice().unwrap(), &[1, 2, 3])
@@ -23,7 +23,7 @@ fn to_pyarray_vec() {
 #[test]
 fn to_pyarray_boxed_slice() {
     Python::with_gil(|py| {
-        let arr = vec![1, 2, 3].into_boxed_slice().to_pyarray_bound(py);
+        let arr = vec![1, 2, 3].into_boxed_slice().to_pyarray(py);
 
         assert_eq!(arr.shape(), [3]);
         assert_eq!(arr.readonly().as_slice().unwrap(), &[1, 2, 3])
@@ -42,7 +42,7 @@ fn to_pyarray_array() {
             .map(|dim| dim * size_of::<f64>() as isize)
             .collect::<Vec<_>>();
 
-        let py_arr = PyArray::from_array_bound(py, &arr);
+        let py_arr = PyArray::from_array(py, &arr);
 
         assert_eq!(py_arr.shape(), shape.as_slice());
         assert_eq!(py_arr.strides(), strides.as_slice());
@@ -52,7 +52,7 @@ fn to_pyarray_array() {
 #[test]
 fn iter_to_pyarray() {
     Python::with_gil(|py| {
-        let arr = PyArray::from_iter_bound(py, (0..10).map(|x| x * x));
+        let arr = PyArray::from_iter(py, (0..10).map(|x| x * x));
 
         assert_eq!(
             arr.readonly().as_slice().unwrap(),
@@ -64,7 +64,7 @@ fn iter_to_pyarray() {
 #[test]
 fn long_iter_to_pyarray() {
     Python::with_gil(|py| {
-        let arr = PyArray::from_iter_bound(py, 0_u32..512);
+        let arr = PyArray::from_iter(py, 0_u32..512);
 
         assert_eq!(
             arr.readonly().as_slice().unwrap(),
@@ -80,7 +80,7 @@ fn from_small_array() {
             $({
                 Python::with_gil(|py| {
                     let array: [$t; 2] = [<$t>::MIN, <$t>::MAX];
-                    let pyarray = array.to_pyarray_bound(py);
+                    let pyarray = array.to_pyarray(py);
 
                     assert_eq!(
                         pyarray.readonly().as_slice().unwrap(),
@@ -97,7 +97,7 @@ fn from_small_array() {
 #[test]
 fn usize_dtype() {
     Python::with_gil(|py| {
-        let x = vec![1_usize, 2, 3].into_pyarray_bound(py);
+        let x = vec![1_usize, 2, 3].into_pyarray(py);
 
         if cfg!(target_pointer_width = "64") {
             py_run!(py, x, "assert str(x.dtype) == 'uint64'")
@@ -110,7 +110,7 @@ fn usize_dtype() {
 #[test]
 fn into_pyarray_vec() {
     Python::with_gil(|py| {
-        let arr = vec![1, 2, 3].into_pyarray_bound(py);
+        let arr = vec![1, 2, 3].into_pyarray(py);
 
         assert_eq!(arr.readonly().as_slice().unwrap(), &[1, 2, 3])
     });
@@ -119,7 +119,7 @@ fn into_pyarray_vec() {
 #[test]
 fn into_pyarray_boxed_slice() {
     Python::with_gil(|py| {
-        let arr = vec![1, 2, 3].into_boxed_slice().into_pyarray_bound(py);
+        let arr = vec![1, 2, 3].into_boxed_slice().into_pyarray(py);
 
         assert_eq!(arr.readonly().as_slice().unwrap(), &[1, 2, 3])
     });
@@ -137,7 +137,7 @@ fn into_pyarray_array() {
             .map(|dim| dim * size_of::<f64>() as isize)
             .collect::<Vec<_>>();
 
-        let py_arr = arr.into_pyarray_bound(py);
+        let py_arr = arr.into_pyarray(py);
 
         assert_eq!(py_arr.shape(), shape.as_slice());
         assert_eq!(py_arr.strides(), strides.as_slice());
@@ -147,7 +147,7 @@ fn into_pyarray_array() {
 #[test]
 fn into_pyarray_cannot_resize() {
     Python::with_gil(|py| {
-        let arr = vec![1, 2, 3].into_pyarray_bound(py);
+        let arr = vec![1, 2, 3].into_pyarray(py);
 
         unsafe {
             assert!(arr.resize(100).is_err());
@@ -158,7 +158,7 @@ fn into_pyarray_cannot_resize() {
 #[test]
 fn into_pyarray_can_write() {
     Python::with_gil(|py| {
-        let arr = vec![1, 2, 3].into_pyarray_bound(py);
+        let arr = vec![1, 2, 3].into_pyarray(py);
 
         py_run!(py, arr, "assert arr.flags['WRITEABLE']");
         py_run!(py, arr, "arr[1] = 4");
@@ -175,7 +175,7 @@ fn collapsed_into_pyarray() {
         arr.slice_collapse(s![1.., ..]);
         let cloned_arr = arr.clone();
 
-        let py_arr = arr.into_pyarray_bound(py);
+        let py_arr = arr.into_pyarray(py);
 
         assert_eq!(py_arr.readonly().as_array(), cloned_arr);
     });
@@ -187,7 +187,7 @@ fn sliced_to_pyarray() {
         let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
         let sliced_matrix = matrix.slice(s![1..4; -1, ..]);
 
-        let py_arr = sliced_matrix.to_pyarray_bound(py);
+        let py_arr = sliced_matrix.to_pyarray(py);
 
         assert_eq!(py_arr.readonly().as_array(), array![[6, 7], [4, 5], [2, 3]],);
 
@@ -201,7 +201,7 @@ fn forder_to_pyarray() {
         let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
         let forder_matrix = matrix.reversed_axes();
 
-        let py_arr = forder_matrix.to_pyarray_bound(py);
+        let py_arr = forder_matrix.to_pyarray(py);
 
         assert_eq!(
             py_arr.readonly().as_array(),
@@ -218,7 +218,7 @@ fn forder_into_pyarray() {
         let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
         let forder_matrix = matrix.reversed_axes();
 
-        let py_arr = forder_matrix.into_pyarray_bound(py);
+        let py_arr = forder_matrix.into_pyarray(py);
 
         assert_eq!(
             py_arr.readonly().as_array(),
@@ -232,12 +232,12 @@ fn forder_into_pyarray() {
 #[test]
 fn to_pyarray_object_vec() {
     Python::with_gil(|py| {
-        let dict = PyDict::new_bound(py);
-        let string = PyString::new_bound(py, "Hello:)");
+        let dict = PyDict::new(py);
+        let string = PyString::new(py, "Hello:)");
         #[allow(clippy::useless_vec)] // otherwise we do not test the right trait impl
-        let vec = vec![dict.to_object(py), string.to_object(py)];
+        let vec = vec![dict.into_any().unbind(), string.into_any().unbind()];
 
-        let arr = vec.to_pyarray_bound(py);
+        let arr = vec.to_pyarray(py);
 
         for (a, b) in vec.iter().zip(arr.readonly().as_slice().unwrap().iter()) {
             assert_eq!(
@@ -252,10 +252,10 @@ fn to_pyarray_object_vec() {
 fn to_pyarray_object_array() {
     Python::with_gil(|py| {
         let mut nd_arr = Array2::from_shape_fn((2, 3), |(_, _)| py.None());
-        nd_arr[(0, 2)] = PyDict::new_bound(py).to_object(py);
-        nd_arr[(1, 0)] = PyString::new_bound(py, "Hello:)").to_object(py);
+        nd_arr[(0, 2)] = PyDict::new(py).into_any().unbind();
+        nd_arr[(1, 0)] = PyString::new(py, "Hello:)").into_any().unbind();
 
-        let py_arr = nd_arr.to_pyarray_bound(py);
+        let py_arr = nd_arr.to_pyarray(py);
 
         for (a, b) in nd_arr
             .as_slice()
@@ -275,16 +275,16 @@ fn to_pyarray_object_array() {
 fn slice_container_type_confusion() {
     Python::with_gil(|py| {
         let mut nd_arr = Array2::from_shape_fn((2, 3), |(_, _)| py.None());
-        nd_arr[(0, 2)] = PyDict::new_bound(py).to_object(py);
-        nd_arr[(1, 0)] = PyString::new_bound(py, "Hello:)").to_object(py);
+        nd_arr[(0, 2)] = PyDict::new(py).into_any().unbind();
+        nd_arr[(1, 0)] = PyString::new(py, "Hello:)").into_any().unbind();
 
-        let _py_arr = nd_arr.into_pyarray_bound(py);
+        let _py_arr = nd_arr.into_pyarray(py);
 
         // Dropping `_py_arr` used to trigger a segmentation fault due to calling `Py_DECREF`
         // on 1, 2 and 3 interpreted as pointers into the Python heap
         // after having created a `SliceBox<PyObject>` backing `_py_arr`,
         // c.f. https://github.com/PyO3/rust-numpy/issues/232.
-        let _py_arr = vec![1, 2, 3].into_pyarray_bound(py);
+        let _py_arr = vec![1, 2, 3].into_pyarray(py);
     });
 }
 
@@ -295,7 +295,7 @@ fn matrix_to_numpy() {
     assert!(nalgebra::RawStorage::is_contiguous(&matrix.data));
 
     Python::with_gil(|py| {
-        let array = matrix.to_pyarray_bound(py);
+        let array = matrix.to_pyarray(py);
 
         assert_eq!(
             array.readonly().as_array(),
@@ -307,7 +307,7 @@ fn matrix_to_numpy() {
     assert!(!nalgebra::RawStorage::is_contiguous(&matrix.data));
 
     Python::with_gil(|py| {
-        let array = matrix.to_pyarray_bound(py);
+        let array = matrix.to_pyarray(py);
 
         assert_eq!(array.readonly().as_array(), array![[0, 1, 2]]);
     });
@@ -315,7 +315,7 @@ fn matrix_to_numpy() {
     let vector = nalgebra::Vector4::<i32>::new(-4, 1, 2, 3);
 
     Python::with_gil(|py| {
-        let array = vector.to_pyarray_bound(py);
+        let array = vector.to_pyarray(py);
 
         assert_eq!(array.readonly().as_array(), array![[-4], [1], [2], [3]]);
     });
@@ -323,7 +323,7 @@ fn matrix_to_numpy() {
     let vector = nalgebra::RowVector2::<i32>::new(23, 42);
 
     Python::with_gil(|py| {
-        let array = vector.to_pyarray_bound(py);
+        let array = vector.to_pyarray(py);
 
         assert_eq!(array.readonly().as_array(), array![[23, 42]]);
     });