【问题标题】:How to stop numpy from iterating over custom Mapping objects?如何阻止 numpy 迭代自定义映射对象?
【发布时间】:2022-01-24 08:49:32
【问题描述】:

问题

嗨。请考虑此代码

import numpy as np


class MappingFoo:
    bar = {"a": 1}

    __len__ = bar.__len__
    __iter__ = bar.__iter__
    __getitem__ = bar.__getitem__


foo = MappingFoo()

print(np.array([foo.bar]))  # [{'a': 1}] (np array with dtype object)

print(np.array([foo]))  # [['a']] (np array with dtype str)

注意:MappingFoo 完全实现了继承并成为 collections.abc.Mapping 所需的所有 3 个魔术方法。

这是我的观察

  • numpy 将MappingFoo 视为列表/可迭代对象。
  • 如果您删除 MappingFoo 的 3 个魔术方法中的任何一个,np.array([foo]) 也将返回一个对象数组,就像第一个 np.array(...) 调用一样。
  • MappingFoo 是否继承自 collections.abc.Mapping 没有区别。

这是我的问题

  1. 在使用numpy.array(...) 创建数组时,numpy 如何决定应该扩展/迭代哪些对象?
  2. 如果我不遍历我的MappingFoo 对象并将它们作为对象存储在数组中,我该如何停止?备注:在我的真实示例中,我事先并不知道输入的类型。因此,我想使用 np.array(...) 来创建数组,而不是依赖 np.empty(...) 然后将其填充到循环或其他需要显式 dtype 的解决方法中。

说明问题 2 的一段代码

俗话说:一段代码值一千字。所以这里用代码重新表述问题 2。

from random import (
    randint,
    random,
)

def generate_input():
    """Generate a non-deterministic list as input for np.array.

    Generate a list or list of lists or 3-times nested list with either
        * only integers
        * a mixture of integers and MappingFoo objects
        * only MappingFoo objects
    """
    def generator(shape_, foo_it_all):
        if len(shape_) == 1:
            return [randint(0, 255) 
                    if not foo_it_all and random() < .99
                    else MappingFoo()
                    for _ in range(shape_[0])]
        else:
            return [generator(shape_[1:], foo_it_all)
                    for _ in range(shape_[0])]

    dim = randint(1, 3)
    shape = tuple(randint(1, 10) for _ in range(dim))
    return shape, generator(shape, random() > .5)
        

shape, input_ = generate_input()
array = np.array(input_)
print(array)
print(array.dtype)
print(shape)
assert array.shape == shape

如何在不更改generate_input 或使用shape 的情况下使断言每次都通过?更改 MappingFoo 就可以了(当然不会删除功能)。最后,array 应该具有保持序列中对象所需的最小 dtype。

【问题讨论】:

  • 您似乎找到了np.array 的控制限制。显然,使用这些方法,输入有资格作为“任何(嵌套)序列”。正如我(和其他人)所展示的,empty/fill 是制作具有指定形状的对象 dtype 数组的唯一可靠方法。否则,您将受到np.array's 的摆布,尝试创建一个多维(数字)数组。
  • "MappingFoo 完全实现了成为collections.abc.Mapping 所需的所有 3 种魔术方法。"请注意,它没有。它实现了 collections.abc.Mapping继承时所需的所有抽象方法。 Mapping 还必须提供 __contains__keysitemsvaluesget__eq____ne__。即使那样,Mapping 也不是结构性的——仅仅提供方法还不足以满足isinstance(foo, Mapping);该类必须继承自 registerMapping
  • 确实如此。我说的很糟糕。我的意思是:它实现了collections.abc.Mapping 所需的方法。当然它不会是它的子类,除非它是它的子类。如果该类没有执行此操作的元类(或通过其他外部方式),其余的魔术方法将不会神奇地出现。我将问题编辑得更准确。谢谢。
  • 实际上有各种在结构上起作用的 ABC——例如HashableContainerGenerator——这样实现“他们的”方法就足以创建一个子类,即使没有继承。不过,Mapping 不是其中之一,它的三个抽象特殊方法对于 Mapping 并不特别——例如,list 也有它们。

标签: python python-3.x numpy


【解决方案1】:

缺少几乎让我退出的导入:

In [31]: from random import random, randint

千字代码只取决于用户是否愿意运行它。尽管如此,提供一些示例输出是个好主意,而不仅仅是代码。

例如,我第一次尝试这个时,我得到了一个 (7, 2, 3) 形状。原因并不明显。

下一个简单的形状:

In [32]: shape,x = generate_input()
In [33]: shape
Out[33]: (1,)
In [34]: x
Out[34]: [169]
In [35]: np.array(x)
Out[35]: array([169])

再次:

In [36]: shape,x = generate_input()
In [37]: shape
Out[37]: (4, 9)
In [38]: x
Out[38]: 
[[<__main__.MappingFoo at 0x7f8e123d81f0>,
  <__main__.MappingFoo at 0x7f8e238906a0>,
  <__main__.MappingFoo at 0x7f8e23890e50>,
  <__main__.MappingFoo at 0x7f8e23890ac0>,
  ...
  <__main__.MappingFoo at 0x7f8e123e67f0>,
  <__main__.MappingFoo at 0x7f8e123e6970>,
  <__main__.MappingFoo at 0x7f8e123e6100>]]



In [39]: np.array(x).shape
Out[39]: (4, 9, 1)    # and a dtype='<U1'

使用空/填充:

In [40]: res = np.empty(shape,object)
In [41]: res[:]=x
In [42]: res
Out[42]: 
array([[<__main__.MappingFoo object at 0x7f8e123d81f0>,
        <__main__.MappingFoo object at 0x7f8e238906a0>,
        <__main__.MappingFoo object at 0x7f8e23890e50>,
        <__main__.MappingFoo object at 0x7f8e23890ac0>,
        <__main__.MappingFoo object at 0x7f8e23890700>,
         ....
        <__main__.MappingFoo object at 0x7f8e123e67f0>,
        <__main__.MappingFoo object at 0x7f8e123e6970>,
        <__main__.MappingFoo object at 0x7f8e123e6100>]], dtype=object)

由于res.shape 匹配x 列表嵌套,因此无需迭代。 res[:]=x 就够了。有时像这样的分配会引发广播错误,但这里不是这种情况。

就像我评论的那样,empty/fill 是创建具有选定形状的对象 dtype 数组的唯一可靠方法。 np.array 正在努力做自己的事情。

【讨论】:

  • 感谢您的回答。我不想使用形状,因为在我的真实场景中,我不知道形状(如我的问题中所述)。如果我想知道它,我将不得不遍历我想要避免的输入。不过好像没有办法避免……
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-07
  • 1970-01-01
  • 1970-01-01
  • 2017-06-20
  • 2019-12-26
  • 2020-04-23
  • 2012-07-08
相关资源
最近更新 更多