【发布时间】:2017-03-15 16:13:06
【问题描述】:
在 numpy 中,许多对象的构造函数接受“array_like”作为第一个参数。是否有这样的对象的定义,无论是作为抽象元类,还是应该包含方法的文档??
【问题讨论】:
在 numpy 中,许多对象的构造函数接受“array_like”作为第一个参数。是否有这样的对象的定义,无论是作为抽象元类,还是应该包含方法的文档??
【问题讨论】:
我将此作为评论发布,但我想我也会将其作为答案。
事实证明,“类数组”的概念与其说是抽象基类或协议,不如说是关于如何处理各种对象的类数组。也就是说,类数组是关于当对象作为类数组参数提供时将如何处理该对象的声明。但几乎任何对象都可以作为类似数组的参数提供。
对于许多对象,类似数组的处理方式与可迭代对象大致相同。序列对象也是如此:
>>> x=[1,2,3]
>>> a = np.array(x)
>>> a[0]
1
但是,numpy 通常不会以相同的方式处理可迭代对象(当作为类似数组的参数提供时)。相反,它将类数组对象视为嵌套对象或原子对象,具体取决于类型。
以下是一些可迭代对象的示例(这个想法听起来与类数组密切相关,但完全不同),但 numpy 将其视为原子对象。
str 对象)array() 工厂将所有这些都视为原子(即非嵌套)值。简而言之:类数组绝不是typing.Iterable 的同义词。
【讨论】:
NumPy 1.21 引入了numpy.typing.ArrayLike。
原来在这个commit中定义如下:
class _SupportsArray(Protocol):
@overload
def __array__(self, __dtype: DtypeLike = ...) -> ndarray: ...
@overload
def __array__(self, dtype: DtypeLike = ...) -> ndarray: ...
ArrayLike = Union[bool, int, float, complex, _SupportsArray, Sequence]
不过,ArrayLike 的更新定义可以在 numpy/typing/_array_like.py 中找到:
_ArrayLike = Union[
_NestedSequence[_SupportsArray[_DType]],
_NestedSequence[_T],
]
ArrayLike = Union[
_RecursiveSequence,
_ArrayLike[
"dtype[Any]",
Union[bool, int, float, complex, str, bytes]
],
]
【讨论】:
事实证明,从技术上讲,几乎所有东西都是类似数组的。 “类数组”更像是对如何解释输入的陈述,而不是对输入可以是什么的限制;如果参数被记录为类数组,NumPy 将尝试将其解释为数组。
除了the nearly tautological one 之外,没有关于类数组的正式定义——类数组是np.array 可以转换为ndarray 的任何Python 对象。要超越这一点,您需要研究source code。
NPY_NO_EXPORT PyObject *
PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
int max_depth, int flags, PyObject *context)
{
/*
* This is the main code to make a NumPy array from a Python
* Object. It is called from many different places.
*/
PyArrayObject *arr = NULL, *ret;
PyArray_Descr *dtype = NULL;
int ndim = 0;
npy_intp dims[NPY_MAXDIMS];
/* Get either the array or its parameters if it isn't an array */
if (PyArray_GetArrayParamsFromObject(op, newtype,
0, &dtype,
&ndim, dims, &arr, context) < 0) {
Py_XDECREF(newtype);
return NULL;
}
...
特别有趣的是PyArray_GetArrayParamsFromObject,它的cmets枚举了np.array期望的对象类型:
NPY_NO_EXPORT int
PyArray_GetArrayParamsFromObject(PyObject *op,
PyArray_Descr *requested_dtype,
npy_bool writeable,
PyArray_Descr **out_dtype,
int *out_ndim, npy_intp *out_dims,
PyArrayObject **out_arr, PyObject *context)
{
PyObject *tmp;
/* If op is an array */
/* If op is a NumPy scalar */
/* If op is a Python scalar */
/* If op supports the PEP 3118 buffer interface */
/* If op supports the __array_struct__ or __array_interface__ interface */
/*
* If op supplies the __array__ function.
* The documentation says this should produce a copy, so
* we skip this method if writeable is true, because the intent
* of writeable is to modify the operand.
* XXX: If the implementation is wrong, and/or if actual
* usage requires this behave differently,
* this should be changed!
*/
/* Try to treat op as a list of lists */
/* Anything can be viewed as an object, unless it needs to be writeable */
}
所以通过研究源代码我们可以得出一个类似数组的is
__array_struct__ or __array_interface__ interface 的对象,或__array__ function 的任何对象,或object dtype 的 0 维数组。【讨论】:
np.array 将遍历 list(或元组),而不是字典。以及如何使自定义对象的行为更像字典。 np.array([{1:2},{3:4}]) 生成一维对象数组,即使 {1:2} 是可迭代的。
__getitem__ and __len__。换句话说:实现这两个函数的任何东西通常都会作为“类数组”传递。
__array__ 路线,为 EagerTensors 实现它,因此它们可以输入到 NumPy 方法中,c.f. github.com/tensorflow/tensorflow/blob/master/tensorflow/python/…
str 对象)。在 numpy 和 scipy 中,字符串被视为原子,而不是熟悉 python 的人可能期望的序列。其他示例是迭代器和缓冲区/文件处理程序。 array() 工厂将所有这些都视为原子(即非嵌套)值。简而言之:类数组绝不是typing.Iterable 的同义词。
这只是一个概念,除了其他答案中提到的User Guide part中的解释之外,还有一个official statement (in Numpy Glossary):
array_like任何可以解释为 ndarray 的序列。这包括 嵌套列表、元组、标量和现有数组。
所以即使是标量也可以考虑在内,就像np.array(1024)。
【讨论】:
"array-like" 一词在 NumPy 中使用,指的是可以作为第一个参数传递给 numpy.array() 以创建数组 () 的任何内容。
一般来说,在 Python 中以类数组结构排列的数值数据可以通过使用 array() 函数转换为数组。最明显的例子是列表和元组。有关其使用的详细信息,请参阅 array() 的文档。一些对象可能支持数组协议并允许以这种方式转换为数组。找出是否可以使用 array() 将对象转换为 numpy 数组的一种简单方法是简单地以交互方式尝试它,看看它是否有效! (Python 方式)。
欲了解更多信息,请阅读:
【讨论】: