首页 > 解决方案 > 是否有 numpy / ndarrays 的包装器返回每个突变方法的数组?

问题描述

python在对象发生突变后,显然numpy不返回对象的决定是经常带来不便的根源。这个问题将基于一种不同的方法:使用一种builder模式,以便我们可以这样做:

x = np.random.randint(0,10+1,50).sort()[:5]

并从一组随机的十个数字中得到至少五个值。相反,None由于sort. 我想知道是否有一个库可以builder在顶部提供该模式,numpy 或者以某种方式强制所有突变numpy方法返回self而不是None.

对于单个突变,该numpy方法可能就足够了:

x = np.sort(np.random.randint(0,10+1,50))[:5]

但是scale当需要一系列方法时,这种方法就不行了。例如

x = np.resize(np.sort(np.random.randint(0,10+1,50))[:5],[5,1])

这很快变得不仅难以编写而且难以阅读:我们被要求从右到左“由内而外”地编写和阅读代码。

更新下面宣布了“赢家”。稍作修改 - 为简洁起见将 from 重命名ChainWrapper为- 以下是 a和 an ( ) Wrp的用法:listnumpyndarray

Wrp(list(range(0,10,2))).append(8).append(10).insert(0, "hello").reverse().unwrap()[0:2]
# list:[10, 8]
import numpy as np
Wrp(np.linspace(0, 9, 10)).reshape(5, 2)[1:3, 0])
#  np.array: array([2., 4.])

重点是 : appendinsert reverse然后reshape 返回 None 。Wrp 检测到并self返回。我经常想编写一个对列表执行多个操作的 lambda。没有上述内容是不可能做到的。

标签: pythonnumpy

解决方案


不返回变异对象是一种 Python 主义,它提醒您没有创建新对象这一事实。None由于这个原因,几乎所有执行内部突变的标准库函数都会返回。

您可以编写自己的包装器(并使用一些getattr魔法,使其自动化),但这可能不太值得。

编辑:如果你需要这个只是为了链接,你可以做类似的事情

def chain(a, f):
    f(a)
    return a

x = chain(
    np.random.randint(0,10+1,50),
    lambda m: m.sort(),
)[:5]

甚至更高级,

def hyperchain(val, *fs):
    for f in fs:
        res = f(val)
        if res is not None:
            val = res
    return val

让你链接价值返回的东西和无返回的东西:

x = hyperchain(
    np.random.randint(0,10+1,50),
    lambda m: m.sort(),
    lambda m: m[:5],
)

编辑2:这是前面提到的getattr包装想法——并不是说这是一个好主意或完美,但我们开始吧:

from functools import wraps


class ChainWrapper:
    def __init__(self, target):
        self._target = target

    def __getattr__(self, key):
        attr = getattr(self._target, key)
        if callable(attr):

            @wraps(attr)
            def wrapped_func(*args, **kwargs):
                retval = attr(*args, **kwargs)
                if retval is None:
                    retval = self
                return retval

            return wrapped_func
        return attr

    def __str__(self):
        return self._target.__str__()

    def __repr__(self):
        return f"<chain-wrapped {self._target!r}"

    def unwrap(self):
        return self._target

    # TODO: implement other things such as __getitem__ and __setitem__
    #       to just proxy through


l = [1, 2, 4, 8]

lw = ChainWrapper(l)
print(lw.append(8).append(10).insert(0, "hello").reverse())

这输出

[10, 8, 8, 4, 2, 1, 'hello']

推荐阅读