Установка подраздела или части глобального массива с помощью объекта Python - PullRequest
3 голосов
/ 20 февраля 2012

Я пытаюсь сослаться на фрагмент «глобального» массива с помощью атрибута объекта. Вот то, на что, я думаю, будет похожа структура класса, и это вариант использования.

import numpy

class X:

    def __init__(self, parent):
        self.parent = parent
        self.pid = [0, 1, 2]

    def __getattr__(self, name):
        if name == 'values':
            return self.parent.P[self.pid]
        else:
            raise AttributeError


class Node:

    def __init__(self):
        self.P = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
        self._values = X(self)

    def __getattr__(self, name):
        if name == 'x':
            return self._values.values
        else:
            raise AttributeError

Вот пример использования:

>>> n = Node()
>>> print n.P
[ 1  2  3  4  5  6  7  8  9 10]
>>> print n.x
[1 2 3]
>>> print n.x[1:3]
[2 3]

Что отлично работает, теперь я хотел бы присвоить значения n.P через атрибут n.x,

>>> n.x = numpy.array([11, 12, 13])

чтобы получить,

>>> print n.P
[ 11  12  13  4  5  6  7  8  9 10]

Или назначить значения срезами,

>>> n.x[1:3] = numpy.array([77, 88])

чтобы получить,

>>> print n.P
[ 11  77  88  4  5  6  7  8  9 10]

Но ради жизни я изо всех сил стараюсь, чтобы это задание работало. Я думал, что будет легко использовать __setattr__ и __setitem__, но целый день я все еще не справился с этим.

В конечном итоге, n.x будет возвращен в виде многомерного массива, в котором класс X изменится по возвращении, но будет сохранен в n.P, который является вектором. Я удалил это, чтобы упростить проблему.

Я бы хотел помочь с этим. Кто-нибудь делал это раньше? Или подсказать как это сделать?

Заранее спасибо за помощь.

РЕШЕНИЕ

Так что после многих дней спотыкания я нашел решение. Я подозреваю, что это может быть упрощено и улучшено. Решение состоит в том, чтобы создать объект X в вашем объекте Node. Когда он извлекается, он возвращает временный объект numpy (значения) со знанием его родительского узла и pids. Функция setslice _ определяется в этом обновлении глобального массива P с новыми значениями. Если объект X назначен, он не возвращает объект Values, а непосредственно устанавливает глобальные значения P.

Две точки, которые могут быть недействительными: 1. Объекты Node и X должны были быть подклассом object ; 2. если вы устанавливаете массив с более высокой размерностью, вам нужно вместо этого использовать __setitem__, который не будет работать с массивами или списками 1D.

Как я уже сказал, я подозреваю, что этот код можно улучшить, так как я не уверен, что полностью его понимаю. Я рад принимать улучшения и предложения.

Спасибо за вашу помощь, особенно Баго.

Вот мой окончательный код.

import numpy

class Values(numpy.ndarray):

    def __new__(cls, input_array, node, pids):
        obj = numpy.asarray(input_array).view(cls)
        obj.node = node
        obj.pids = pids
        return obj

    def __setslice__(self, i, j, values):
        self.node._set_values(self.pids[i:j], values)


class X(object):

    def __get__(self, instance, owner):
        p = instance.P[instance.pids]
        return Values(p, instance, instance.pids)

    def __set__(self, instance, values):
        instance.P[instance.pids] = values

class Node(object):

    x = X()

    def __init__(self, pids=[0, 1, 2]):
        self.P = numpy.arange(11)
        self.pids = pids

    def _set_values(self, pids, values):
        self.P[pids] = values


node = Node(pids=[4, 5, 6, 7])
print '\nInitial State:'
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]

print '\nSetting node.x = [44, 55, 66, 77]:'
node.x = [44, 55, 66, 77]
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]

print '\nSetting node.x[1:3] = [100, 200]:'
node.x[1:3] = [100, 200]
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]

1 Ответ

0 голосов
/ 20 февраля 2012

Мне не понятно, что не работает, но я думаю, может быть, вы пытаетесь сделать что-то вроде этого:

import numpy

class X(object):

    def __init__(self, parent):
        self.parent = parent
        self.pid = [0, 1, 2]

    @property
    def values(self):
        tmp = self.parent.P[self.pid]
        return tmp
    @values.setter
    def values(self, input):
        self.parent.P[self.pid] = input

class Node(object):

    def __init__(self):
        self.P = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
        self._values = X(self)

    @property
    def x(self):
        return self._values.values
    @x.setter
    def x(self, input):
        self._values.values = input

Я надеюсь, что вы начали.

обновление

Причина, по которой n.x[1:3] = [77, 88] не работает с использованием этого подхода, заключается в том, что n.x и n.x[:] = ~ оба вызывают метод get X.values, который возвращает tmp. Но tmp является копией части P и после n.x[:] = ~ tmp выбрасывается, а P не обновляется. tmp является копией, потому что когда вы индексируете массив с другим массивом, вы получаете копию, а не представление. Вот пример, чтобы прояснить это, вы можете прочитать больше о нарезке / индексировании numy здесь .

>>> P = np.arange(10)
>>> pid = np.array([1, 2, 3])
>>> Q = P[pid]
>>> Q[:] = 99
>>> P
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> R = P[1:4]
>>> R[:] = 99
>>> P
array([ 0, 99, 99, 99,  4,  5,  6,  7,  8,  9])
>>> P[[1,2]][:] = 88
>>> P
array([ 0, 99, 99, 99,  4,  5,  6,  7,  8,  9])

setitem не поможет, потому что вы вызываете метод setitem tmp, а не X.

Самый простой способ заставить его работать - это заменить массив pid слайсом, но я знаю, что это своего рода ограничение. Вы также можете отслеживать массив tmp, иметь self._tmp, чтобы потом можно было перемещать значения из _tmp в P. Я знаю, что ни один из них не идеален, но, возможно, кто-то еще придет с лучшим подходом. Извините, я не мог сделать больше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...