Существует ли оболочка для numpy / ndarrays, которая возвращает массив для каждого из методов мутации? - PullRequest
0 голосов
/ 15 марта 2020

Решение python и, очевидно, numpy не возвращать объект после его мутации является источником частых неудобств. Этот вопрос будет основан на другом подходе: использовать шаблон builder, чтобы мы могли сделать это:

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

и получить как минимум пять значений из случайного набора из десяти чисел. Вместо этого вышеприведенное приводит к ошибке времени выполнения из-за подписки None после sort. Мне интересно, была ли библиотека, предоставляющая этот шаблон builder поверх numpy, или какой-нибудь способ принудительно all mutation 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])

Это быстро становится трудным не только для записи, но и для чтения : нас просят как написать, так и прочитать код "наизнанку" справа налево.

Обновление"Победитель" был объявлен ниже. Внесение небольшой модификации - переименование с ChainWrapper на Wrp только для краткости - здесь используется как list, так и (numpy) ndarray:

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.])

Точка is: append, insert reverse и reshape return None. Wrp обнаруживает это и возвращает self. Часто я хочу написать лямбду, которая выполняет более одной операции в списке. Без вышесказанного сделать это было невозможно.

1 Ответ

2 голосов
/ 15 марта 2020

Не возвращать мутированный объект - это Pythonism, который напоминает вам о том факте, что новый объект не был создан. По этой причине практически все стандартные библиотечные функции, которые выполняют внутреннюю мутацию, возвращают None.

Вы можете написать свою собственную оболочку (а с некоторыми getattr magi c, сделать ее автоматизированной c) , но это, вероятно, не совсем стоит.

РЕДАКТИРОВАТЬ: Если вам нужно это только для цепочки, вы могли бы сделать что-то вроде

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 идея обертки - не говоря, что это хорошая идея или идеальная, но здесь мы go:

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']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...