diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py
index c7230dd7385c2..21d12d02c9008 100644
--- a/pandas/core/algorithms.py
+++ b/pandas/core/algorithms.py
@@ -50,6 +50,7 @@
 from pandas.core.dtypes.missing import isna, na_value_for_dtype
 
 from pandas.core import common as com
+from pandas.core.construction import array
 from pandas.core.indexers import validate_indices
 
 _shared_docs = {}  # type: Dict[str, str]
@@ -1855,8 +1856,6 @@ def searchsorted(arr, value, side="left", sorter=None):
         and is_integer_dtype(arr)
         and (is_integer(value) or is_integer_dtype(value))
     ):
-        from .arrays.array_ import array
-
         # if `arr` and `value` have different dtypes, `arr` would be
         # recast by numpy, causing a slow search.
         # Before searching below, we therefore try to give `value` the
diff --git a/pandas/core/api.py b/pandas/core/api.py
index f3ea0976a2869..73323d93b8215 100644
--- a/pandas/core/api.py
+++ b/pandas/core/api.py
@@ -20,7 +20,8 @@
     IntervalDtype,
     DatetimeTZDtype,
 )
-from pandas.core.arrays import Categorical, array
+from pandas.core.arrays import Categorical
+from pandas.core.construction import array
 from pandas.core.groupby import Grouper, NamedAgg
 from pandas.io.formats.format import set_eng_float_format
 from pandas.core.index import (
diff --git a/pandas/core/arrays/__init__.py b/pandas/core/arrays/__init__.py
index dab29e9ce71d3..5c83ed8cf5e24 100644
--- a/pandas/core/arrays/__init__.py
+++ b/pandas/core/arrays/__init__.py
@@ -1,4 +1,3 @@
-from .array_ import array  # noqa: F401
 from .base import (  # noqa: F401
     ExtensionArray,
     ExtensionOpsMixin,
diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py
index 6200cd14663f8..8b0325748a353 100644
--- a/pandas/core/arrays/categorical.py
+++ b/pandas/core/arrays/categorical.py
@@ -60,6 +60,7 @@
 )
 from pandas.core.base import NoNewAttributesMixin, PandasObject, _shared_docs
 import pandas.core.common as com
+from pandas.core.construction import extract_array, sanitize_array
 from pandas.core.missing import interpolate_2d
 from pandas.core.sorting import nargsort
 
@@ -374,7 +375,6 @@ def __init__(
             values = maybe_infer_to_datetimelike(values, convert_dates=True)
             if not isinstance(values, np.ndarray):
                 values = _convert_to_list_like(values)
-                from pandas.core.internals.construction import sanitize_array
 
                 # By convention, empty lists result in object dtype:
                 if len(values) == 0:
@@ -2162,8 +2162,6 @@ def __setitem__(self, key, value):
             If (one or more) Value is not in categories or if a assigned
             `Categorical` does not have the same categories
         """
-        from pandas.core.internals.arrays import extract_array
-
         value = extract_array(value, extract_numpy=True)
 
         # require identical categories set
@@ -2526,8 +2524,6 @@ def isin(self, values):
         >>> s.isin(['lama'])
         array([ True, False,  True, False,  True, False])
         """
-        from pandas.core.internals.construction import sanitize_array
-
         if not is_list_like(values):
             raise TypeError(
                 "only list-like objects are allowed to be passed"
diff --git a/pandas/core/arrays/numpy_.py b/pandas/core/arrays/numpy_.py
index 77c9a3bc98690..1c35298fcc6b8 100644
--- a/pandas/core/arrays/numpy_.py
+++ b/pandas/core/arrays/numpy_.py
@@ -16,6 +16,7 @@
 from pandas import compat
 from pandas.core import nanops
 from pandas.core.algorithms import searchsorted, take, unique
+from pandas.core.construction import extract_array
 from pandas.core.missing import backfill_1d, pad_1d
 
 from .base import ExtensionArray, ExtensionOpsMixin
@@ -222,8 +223,6 @@ def __getitem__(self, item):
         return result
 
     def __setitem__(self, key, value):
-        from pandas.core.internals.arrays import extract_array
-
         value = extract_array(value, extract_numpy=True)
 
         if not lib.is_scalar(key) and is_list_like(key):
diff --git a/pandas/core/arrays/sparse.py b/pandas/core/arrays/sparse.py
index 9376b49112f6f..b8f41b140b245 100644
--- a/pandas/core/arrays/sparse.py
+++ b/pandas/core/arrays/sparse.py
@@ -52,6 +52,7 @@
 from pandas.core.arrays import ExtensionArray, ExtensionOpsMixin
 from pandas.core.base import PandasObject
 import pandas.core.common as com
+from pandas.core.construction import sanitize_array
 from pandas.core.missing import interpolate_2d
 import pandas.core.ops as ops
 
@@ -664,7 +665,6 @@ def __init__(
         if not is_array_like(data):
             try:
                 # probably shared code in sanitize_series
-                from pandas.core.internals.construction import sanitize_array
 
                 data = sanitize_array(data, index=None)
             except ValueError:
diff --git a/pandas/core/arrays/array_.py b/pandas/core/construction.py
similarity index 50%
rename from pandas/core/arrays/array_.py
rename to pandas/core/construction.py
index 314144db57712..9528723a6dc0f 100644
--- a/pandas/core/arrays/array_.py
+++ b/pandas/core/construction.py
@@ -1,16 +1,51 @@
+"""
+Constructor functions intended to be shared by pd.array, Series.__init__,
+and Index.__new__.
+
+These should not depend on core.internals.
+"""
 from typing import Optional, Sequence, Union, cast
 
 import numpy as np
+import numpy.ma as ma
 
 from pandas._libs import lib, tslibs
-
+from pandas._libs.tslibs import IncompatibleFrequency, OutOfBoundsDatetime
+
+from pandas.core.dtypes.cast import (
+    construct_1d_arraylike_from_scalar,
+    construct_1d_ndarray_preserving_na,
+    construct_1d_object_array_from_listlike,
+    infer_dtype_from_scalar,
+    maybe_cast_to_datetime,
+    maybe_cast_to_integer_array,
+    maybe_castable,
+    maybe_convert_platform,
+    maybe_upcast,
+)
 from pandas.core.dtypes.common import (
+    is_categorical_dtype,
     is_datetime64_ns_dtype,
     is_extension_array_dtype,
+    is_extension_type,
+    is_float_dtype,
+    is_integer_dtype,
+    is_iterator,
+    is_list_like,
+    is_object_dtype,
     is_timedelta64_ns_dtype,
+    pandas_dtype,
 )
 from pandas.core.dtypes.dtypes import ExtensionDtype, registry
-from pandas.core.dtypes.generic import ABCExtensionArray
+from pandas.core.dtypes.generic import (
+    ABCExtensionArray,
+    ABCIndexClass,
+    ABCPandasArray,
+    ABCSeries,
+)
+from pandas.core.dtypes.missing import isna
+
+import pandas.core.common as com
 
 
 def array(
@@ -217,7 +252,6 @@ def array(
         DatetimeArray,
         TimedeltaArray,
     )
-    from pandas.core.internals.arrays import extract_array
 
     if lib.is_scalar(data):
         msg = "Cannot pass scalar '{}' to 'pandas.array'."
@@ -278,3 +312,241 @@ def array(
 
     result = PandasArray._from_sequence(data, dtype=dtype, copy=copy)
     return result
+
+
+def extract_array(obj, extract_numpy=False):
+    """
+    Extract the ndarray or ExtensionArray from a Series or Index.
+
+    For all other types, `obj` is just returned as is.
+
+    Parameters
+    ----------
+    obj : object
+        For Series / Index, the underlying ExtensionArray is unboxed.
+        For Numpy-backed ExtensionArrays, the ndarray is extracted.
+
+    extract_numpy : bool, default False
+        Whether to extract the ndarray from a PandasArray
+
+    Returns
+    -------
+    arr : object
+
+    Examples
+    --------
+    >>> extract_array(pd.Series(['a', 'b', 'c'], dtype='category'))
+    [a, b, c]
+    Categories (3, object): [a, b, c]
+
+    Other objects like lists, arrays, and DataFrames are just passed through.
+
+    >>> extract_array([1, 2, 3])
+    [1, 2, 3]
+
+    For an ndarray-backed Series / Index a PandasArray is returned.
+
+    >>> extract_array(pd.Series([1, 2, 3]))
+    <PandasArray>
+    [1, 2, 3]
+    Length: 3, dtype: int64
+
+    To extract all the way down to the ndarray, pass ``extract_numpy=True``.
+
+    >>> extract_array(pd.Series([1, 2, 3]), extract_numpy=True)
+    array([1, 2, 3])
+    """
+    if isinstance(obj, (ABCIndexClass, ABCSeries)):
+        obj = obj.array
+
+    if extract_numpy and isinstance(obj, ABCPandasArray):
+        obj = obj.to_numpy()
+
+    return obj
+
+
+def sanitize_array(data, index, dtype=None, copy=False, raise_cast_failure=False):
+    """
+    Sanitize input data to an ndarray, copy if specified, coerce to the
+    dtype if specified.
+    """
+    if dtype is not None:
+        dtype = pandas_dtype(dtype)
+
+    if isinstance(data, ma.MaskedArray):
+        mask = ma.getmaskarray(data)
+        if mask.any():
+            data, fill_value = maybe_upcast(data, copy=True)
+            data.soften_mask()  # set hardmask False if it was True
+            data[mask] = fill_value
+        else:
+            data = data.copy()
+
+    # extract ndarray or ExtensionArray, ensure we have no PandasArray
+    data = extract_array(data, extract_numpy=True)
+
+    # GH#846
+    if isinstance(data, np.ndarray):
+
+        if dtype is not None and is_float_dtype(data.dtype) and is_integer_dtype(dtype):
+            # possibility of nan -> garbage
+            try:
+                subarr = _try_cast(data, dtype, copy, True)
+            except ValueError:
+                if copy:
+                    subarr = data.copy()
+                else:
+                    subarr = np.array(data, copy=False)
+        else:
+            # we will try to copy be-definition here
+            subarr = _try_cast(data, dtype, copy, raise_cast_failure)
+
+    elif isinstance(data, ABCExtensionArray):
+        # it is already ensured above this is not a PandasArray
+        subarr = data
+
+        if dtype is not None:
+            subarr = subarr.astype(dtype, copy=copy)
+        elif copy:
+            subarr = subarr.copy()
+        return subarr
+
+    elif isinstance(data, (list, tuple)) and len(data) > 0:
+        if dtype is not None:
+            try:
+                subarr = _try_cast(data, dtype, copy, raise_cast_failure)
+            except Exception:
+                if raise_cast_failure:  # pragma: no cover
+                    raise
+                subarr = np.array(data, dtype=object, copy=copy)
+                subarr = lib.maybe_convert_objects(subarr)
+
+        else:
+            subarr = maybe_convert_platform(data)
+
+        subarr = maybe_cast_to_datetime(subarr, dtype)
+
+    elif isinstance(data, range):
+        # GH#16804
+        arr = np.arange(data.start, data.stop, data.step, dtype="int64")
+        subarr = _try_cast(arr, dtype, copy, raise_cast_failure)
+    else:
+        subarr = _try_cast(data, dtype, copy, raise_cast_failure)
+
+    # scalar like, GH
+    if getattr(subarr, "ndim", 0) == 0:
+        if isinstance(data, list):  # pragma: no cover
+            subarr = np.array(data, dtype=object)
+        elif index is not None:
+            value = data
+
+            # figure out the dtype from the value (upcast if necessary)
+            if dtype is None:
+                dtype, value = infer_dtype_from_scalar(value)
+            else:
+                # need to possibly convert the value here
+                value = maybe_cast_to_datetime(value, dtype)
+
+            subarr = construct_1d_arraylike_from_scalar(value, len(index), dtype)
+
+        else:
+            return subarr.item()
+
+    # the result that we want
+    elif subarr.ndim == 1:
+        if index is not None:
+
+            # a 1-element ndarray
+            if len(subarr) != len(index) and len(subarr) == 1:
+                subarr = construct_1d_arraylike_from_scalar(
+                    subarr[0], len(index), subarr.dtype
+                )
+
+    elif subarr.ndim > 1:
+        if isinstance(data, np.ndarray):
+            raise Exception("Data must be 1-dimensional")
+        else:
+            subarr = com.asarray_tuplesafe(data, dtype=dtype)
+
+    # This is to prevent mixed-type Series getting all casted to
+    # NumPy string type, e.g. NaN --> '-1#IND'.
+    if issubclass(subarr.dtype.type, str):
+        # GH#16605
+        # If not empty convert the data to dtype
+        # GH#19853: If data is a scalar, subarr has already the result
+        if not lib.is_scalar(data):
+            if not np.all(isna(data)):
+                data = np.array(data, dtype=dtype, copy=False)
+            subarr = np.array(data, dtype=object, copy=copy)
+
+    if (
+        not (is_extension_array_dtype(subarr.dtype) or is_extension_array_dtype(dtype))
+        and is_object_dtype(subarr.dtype)
+        and not is_object_dtype(dtype)
+    ):
+        inferred = lib.infer_dtype(subarr, skipna=False)
+        if inferred == "period":
+            from pandas.core.arrays import period_array
+
+            try:
+                subarr = period_array(subarr)
+            except IncompatibleFrequency:
+                pass
+
+    return subarr
+
+
+def _try_cast(arr, dtype, copy, raise_cast_failure):
+    """
+    Convert input to numpy ndarray and optionally cast to a given dtype.
+
+    Parameters
+    ----------
+    arr : array-like
+    dtype : np.dtype, ExtensionDtype or None
+    copy : bool
+        If False, don't copy the data if not needed.
+    raise_cast_failure : bool
+        If True, and if a dtype is specified, raise errors during casting.
+        Otherwise an object array is returned.
+    """
+    # perf shortcut as this is the most common case
+    if isinstance(arr, np.ndarray):
+        if maybe_castable(arr) and not copy and dtype is None:
+            return arr
+
+    try:
+        # GH#15832: Check if we are requesting a numeric dype and
+        # that we can convert the data to the requested dtype.
+        if is_integer_dtype(dtype):
+            subarr = maybe_cast_to_integer_array(arr, dtype)
+
+        subarr = maybe_cast_to_datetime(arr, dtype)
+        # Take care in creating object arrays (but iterators are not
+        # supported):
+        if is_object_dtype(dtype) and (
+            is_list_like(subarr)
+            and not (is_iterator(subarr) or isinstance(subarr, np.ndarray))
+        ):
+            subarr = construct_1d_object_array_from_listlike(subarr)
+        elif not is_extension_type(subarr):
+            subarr = construct_1d_ndarray_preserving_na(subarr, dtype, copy=copy)
+    except OutOfBoundsDatetime:
+        # in case of out of bound datetime64 -> always raise
+        raise
+    except (ValueError, TypeError):
+        if is_categorical_dtype(dtype):
+            # We *do* allow casting to categorical, since we know
+            # that Categorical is the only array type for 'category'.
+            subarr = dtype.construct_array_type()(
+                arr, dtype.categories, ordered=dtype._ordered
+            )
+        elif is_extension_array_dtype(dtype):
+            # create an extension array from its dtype
+            array_type = dtype.construct_array_type()._from_sequence
+            subarr = array_type(arr, dtype=dtype, copy=copy)
+        elif dtype is not None and raise_cast_failure:
+            raise
+        else:
+            subarr = np.array(arr, dtype=object, copy=copy)
+    return subarr
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index 9053edf2d1424..42e636ed2204f 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -6,7 +6,7 @@
 import operator
 import pickle
 from textwrap import dedent
-from typing import Callable, FrozenSet, List, Optional, Set
+from typing import Callable, Dict, FrozenSet, List, Optional, Set
 import warnings
 import weakref
 
@@ -73,7 +73,7 @@
 
 # goal is to be able to define the docs close to function, while still being
 # able to share
-_shared_docs = dict()
+_shared_docs = dict()  # type: Dict[str, str]
 _shared_doc_kwargs = dict(
     axes="keywords for axes",
     klass="Series/DataFrame",
diff --git a/pandas/core/internals/arrays.py b/pandas/core/internals/arrays.py
deleted file mode 100644
index 18af328bfa77f..0000000000000
--- a/pandas/core/internals/arrays.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""
-Methods for cleaning, validating, and unboxing arrays.
-"""
-from pandas.core.dtypes.generic import ABCIndexClass, ABCPandasArray, ABCSeries
-
-
-def extract_array(obj, extract_numpy=False):
-    """
-    Extract the ndarray or ExtensionArray from a Series or Index.
-
-    For all other types, `obj` is just returned as is.
-
-    Parameters
-    ----------
-    obj : object
-        For Series / Index, the underlying ExtensionArray is unboxed.
-        For Numpy-backed ExtensionArrays, the ndarray is extracted.
-
-    extract_numpy : bool, default False
-        Whether to extract the ndarray from a PandasArray
-
-    Returns
-    -------
-    arr : object
-
-    Examples
-    --------
-    >>> extract_array(pd.Series(['a', 'b', 'c'], dtype='category'))
-    [a, b, c]
-    Categories (3, object): [a, b, c]
-
-    Other objects like lists, arrays, and DataFrames are just passed through.
-
-    >>> extract_array([1, 2, 3])
-    [1, 2, 3]
-
-    For an ndarray-backed Series / Index a PandasArray is returned.
-
-    >>> extract_array(pd.Series([1, 2, 3]))
-    <PandasArray>
-    [1, 2, 3]
-    Length: 3, dtype: int64
-
-    To extract all the way down to the ndarray, pass ``extract_numpy=True``.
-
-    >>> extract_array(pd.Series([1, 2, 3]), extract_numpy=True)
-    array([1, 2, 3])
-    """
-    if isinstance(obj, (ABCIndexClass, ABCSeries)):
-        obj = obj.array
-
-    if extract_numpy and isinstance(obj, ABCPandasArray):
-        obj = obj.to_numpy()
-
-    return obj
diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py
index 549920e230e8a..aa22896783b86 100644
--- a/pandas/core/internals/blocks.py
+++ b/pandas/core/internals/blocks.py
@@ -77,12 +77,12 @@
 )
 from pandas.core.base import PandasObject
 import pandas.core.common as com
+from pandas.core.construction import extract_array
 from pandas.core.indexers import (
     check_setitem_lengths,
     is_empty_indexer,
     is_scalar_indexer,
 )
-from pandas.core.internals.arrays import extract_array
 import pandas.core.missing as missing
 from pandas.core.nanops import nanpercentile
 
diff --git a/pandas/core/internals/construction.py b/pandas/core/internals/construction.py
index c437f686bd17b..74b16f0e72883 100644
--- a/pandas/core/internals/construction.py
+++ b/pandas/core/internals/construction.py
@@ -8,18 +8,12 @@
 import numpy.ma as ma
 
 from pandas._libs import lib
-from pandas._libs.tslibs import IncompatibleFrequency, OutOfBoundsDatetime
 import pandas.compat as compat
 from pandas.compat import PY36, raise_with_traceback
 
 from pandas.core.dtypes.cast import (
     construct_1d_arraylike_from_scalar,
-    construct_1d_ndarray_preserving_na,
-    construct_1d_object_array_from_listlike,
-    infer_dtype_from_scalar,
     maybe_cast_to_datetime,
-    maybe_cast_to_integer_array,
-    maybe_castable,
     maybe_convert_platform,
     maybe_infer_to_datetimelike,
     maybe_upcast,
@@ -29,13 +23,9 @@
     is_datetime64tz_dtype,
     is_dtype_equal,
     is_extension_array_dtype,
-    is_extension_type,
-    is_float_dtype,
     is_integer_dtype,
-    is_iterator,
     is_list_like,
     is_object_dtype,
-    pandas_dtype,
 )
 from pandas.core.dtypes.generic import (
     ABCDataFrame,
@@ -45,10 +35,10 @@
     ABCSeries,
     ABCTimedeltaIndex,
 )
-from pandas.core.dtypes.missing import isna
 
 from pandas.core import algorithms, common as com
-from pandas.core.arrays import Categorical, ExtensionArray, period_array
+from pandas.core.arrays import Categorical
+from pandas.core.construction import sanitize_array
 from pandas.core.index import (
     Index,
     _get_objs_combined_axis,
@@ -60,7 +50,6 @@
     create_block_manager_from_arrays,
     create_block_manager_from_blocks,
 )
-from pandas.core.internals.arrays import extract_array
 
 # ---------------------------------------------------------------------
 # BlockManager Interface
@@ -625,186 +614,3 @@ def sanitize_index(data, index, copy=False):
             data = sanitize_array(data, index, copy=copy)
 
     return data
-
-
-def sanitize_array(data, index, dtype=None, copy=False, raise_cast_failure=False):
-    """
-    Sanitize input data to an ndarray, copy if specified, coerce to the
-    dtype if specified.
-    """
-    if dtype is not None:
-        dtype = pandas_dtype(dtype)
-
-    if isinstance(data, ma.MaskedArray):
-        mask = ma.getmaskarray(data)
-        if mask.any():
-            data, fill_value = maybe_upcast(data, copy=True)
-            data.soften_mask()  # set hardmask False if it was True
-            data[mask] = fill_value
-        else:
-            data = data.copy()
-
-    # extract ndarray or ExtensionArray, ensure we have no PandasArray
-    data = extract_array(data, extract_numpy=True)
-
-    # GH#846
-    if isinstance(data, np.ndarray):
-
-        if dtype is not None and is_float_dtype(data.dtype) and is_integer_dtype(dtype):
-            # possibility of nan -> garbage
-            try:
-                subarr = _try_cast(data, dtype, copy, True)
-            except ValueError:
-                if copy:
-                    subarr = data.copy()
-                else:
-                    subarr = np.array(data, copy=False)
-        else:
-            # we will try to copy be-definition here
-            subarr = _try_cast(data, dtype, copy, raise_cast_failure)
-
-    elif isinstance(data, ExtensionArray):
-        # it is already ensured above this is not a PandasArray
-        subarr = data
-
-        if dtype is not None:
-            subarr = subarr.astype(dtype, copy=copy)
-        elif copy:
-            subarr = subarr.copy()
-        return subarr
-
-    elif isinstance(data, (list, tuple)) and len(data) > 0:
-        if dtype is not None:
-            try:
-                subarr = _try_cast(data, dtype, copy, raise_cast_failure)
-            except Exception:
-                if raise_cast_failure:  # pragma: no cover
-                    raise
-                subarr = np.array(data, dtype=object, copy=copy)
-                subarr = lib.maybe_convert_objects(subarr)
-
-        else:
-            subarr = maybe_convert_platform(data)
-
-        subarr = maybe_cast_to_datetime(subarr, dtype)
-
-    elif isinstance(data, range):
-        # GH#16804
-        arr = np.arange(data.start, data.stop, data.step, dtype="int64")
-        subarr = _try_cast(arr, dtype, copy, raise_cast_failure)
-    else:
-        subarr = _try_cast(data, dtype, copy, raise_cast_failure)
-
-    # scalar like, GH
-    if getattr(subarr, "ndim", 0) == 0:
-        if isinstance(data, list):  # pragma: no cover
-            subarr = np.array(data, dtype=object)
-        elif index is not None:
-            value = data
-
-            # figure out the dtype from the value (upcast if necessary)
-            if dtype is None:
-                dtype, value = infer_dtype_from_scalar(value)
-            else:
-                # need to possibly convert the value here
-                value = maybe_cast_to_datetime(value, dtype)
-
-            subarr = construct_1d_arraylike_from_scalar(value, len(index), dtype)
-
-        else:
-            return subarr.item()
-
-    # the result that we want
-    elif subarr.ndim == 1:
-        if index is not None:
-
-            # a 1-element ndarray
-            if len(subarr) != len(index) and len(subarr) == 1:
-                subarr = construct_1d_arraylike_from_scalar(
-                    subarr[0], len(index), subarr.dtype
-                )
-
-    elif subarr.ndim > 1:
-        if isinstance(data, np.ndarray):
-            raise Exception("Data must be 1-dimensional")
-        else:
-            subarr = com.asarray_tuplesafe(data, dtype=dtype)
-
-    # This is to prevent mixed-type Series getting all casted to
-    # NumPy string type, e.g. NaN --> '-1#IND'.
-    if issubclass(subarr.dtype.type, str):
-        # GH#16605
-        # If not empty convert the data to dtype
-        # GH#19853: If data is a scalar, subarr has already the result
-        if not lib.is_scalar(data):
-            if not np.all(isna(data)):
-                data = np.array(data, dtype=dtype, copy=False)
-            subarr = np.array(data, dtype=object, copy=copy)
-
-    if (
-        not (is_extension_array_dtype(subarr.dtype) or is_extension_array_dtype(dtype))
-        and is_object_dtype(subarr.dtype)
-        and not is_object_dtype(dtype)
-    ):
-        inferred = lib.infer_dtype(subarr, skipna=False)
-        if inferred == "period":
-            try:
-                subarr = period_array(subarr)
-            except IncompatibleFrequency:
-                pass
-
-    return subarr
-
-
-def _try_cast(arr, dtype, copy, raise_cast_failure):
-    """
-    Convert input to numpy ndarray and optionally cast to a given dtype.
-
-    Parameters
-    ----------
-    arr : array-like
-    dtype : np.dtype, ExtensionDtype or None
-    copy : bool
-        If False, don't copy the data if not needed.
-    raise_cast_failure : bool
-        If True, and if a dtype is specified, raise errors during casting.
-        Otherwise an object array is returned.
-    """
-    # perf shortcut as this is the most common case
-    if isinstance(arr, np.ndarray):
-        if maybe_castable(arr) and not copy and dtype is None:
-            return arr
-
-    try:
-        # GH#15832: Check if we are requesting a numeric dype and
-        # that we can convert the data to the requested dtype.
-        if is_integer_dtype(dtype):
-            subarr = maybe_cast_to_integer_array(arr, dtype)
-
-        subarr = maybe_cast_to_datetime(arr, dtype)
-        # Take care in creating object arrays (but iterators are not
-        # supported):
-        if is_object_dtype(dtype) and (
-            is_list_like(subarr)
-            and not (is_iterator(subarr) or isinstance(subarr, np.ndarray))
-        ):
-            subarr = construct_1d_object_array_from_listlike(subarr)
-        elif not is_extension_type(subarr):
-            subarr = construct_1d_ndarray_preserving_na(subarr, dtype, copy=copy)
-    except OutOfBoundsDatetime:
-        # in case of out of bound datetime64 -> always raise
-        raise
-    except (ValueError, TypeError):
-        if is_categorical_dtype(dtype):
-            # We *do* allow casting to categorical, since we know
-            # that Categorical is the only array type for 'category'.
-            subarr = Categorical(arr, dtype.categories, ordered=dtype._ordered)
-        elif is_extension_array_dtype(dtype):
-            # create an extension array from its dtype
-            array_type = dtype.construct_array_type()._from_sequence
-            subarr = array_type(arr, dtype=dtype, copy=copy)
-        elif dtype is not None and raise_cast_failure:
-            raise
-        else:
-            subarr = np.array(arr, dtype=object, copy=copy)
-    return subarr
diff --git a/pandas/core/reshape/reshape.py b/pandas/core/reshape/reshape.py
index 8d5b521ef7799..1f519d4c0867d 100644
--- a/pandas/core/reshape/reshape.py
+++ b/pandas/core/reshape/reshape.py
@@ -22,9 +22,9 @@
 import pandas.core.algorithms as algos
 from pandas.core.arrays import SparseArray
 from pandas.core.arrays.categorical import _factorize_from_iterable
+from pandas.core.construction import extract_array
 from pandas.core.frame import DataFrame
 from pandas.core.index import Index, MultiIndex
-from pandas.core.internals.arrays import extract_array
 from pandas.core.series import Series
 from pandas.core.sorting import (
     compress_group_index,
diff --git a/pandas/core/series.py b/pandas/core/series.py
index 418b3fc8c57d0..dcf9dd4def9e5 100644
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -61,6 +61,7 @@
 from pandas.core.arrays.categorical import Categorical, CategoricalAccessor
 from pandas.core.arrays.sparse import SparseAccessor
 import pandas.core.common as com
+from pandas.core.construction import extract_array, sanitize_array
 from pandas.core.index import (
     Float64Index,
     Index,
@@ -76,7 +77,6 @@
 from pandas.core.indexes.timedeltas import TimedeltaIndex
 from pandas.core.indexing import check_bool_indexer
 from pandas.core.internals import SingleBlockManager
-from pandas.core.internals.construction import sanitize_array
 from pandas.core.strings import StringMethods
 from pandas.core.tools.datetimes import to_datetime
 
@@ -801,8 +801,6 @@ def __array_ufunc__(
         self, ufunc: Callable, method: str, *inputs: Any, **kwargs: Any
     ):
         # TODO: handle DataFrame
-        from pandas.core.internals.construction import extract_array
-
         cls = type(self)
 
         # for binary ops, use our custom dunder methods
diff --git a/pandas/core/sorting.py b/pandas/core/sorting.py
index 5f3ed87424d0e..454156fd97344 100644
--- a/pandas/core/sorting.py
+++ b/pandas/core/sorting.py
@@ -15,6 +15,7 @@
 from pandas.core.dtypes.missing import isna
 
 import pandas.core.algorithms as algorithms
+from pandas.core.construction import extract_array
 
 _INT64_MAX = np.iinfo(np.int64).max
 
@@ -240,8 +241,6 @@ def nargsort(items, kind="quicksort", ascending=True, na_position="last"):
     handles NaNs. It adds ascending and na_position parameters.
     GH #6399, #5231
     """
-    from pandas.core.internals.arrays import extract_array
-
     items = extract_array(items)
     mask = np.asarray(isna(items))