Почему .append() не работает в этом списке?

У меня есть объект scene, который является экземпляром класса Scene и имеет список children, который возвращает:

[<pythreejs.pythreejs.Mesh object at 0x000000002E836A90>, <pythreejs.pythreejs.SurfaceGrid object at 0x000000002DBF9F60>, <pythreejs.pythreejs.Mesh object at 0x000000002E8362E8>, <pythreejs.pythreejs.AmbientLight object at 0x000000002E8366D8>, <pythreejs.pythreejs.DirectionalLight object at 0x000000002E836630>]

Если я хочу обновить этот список с помощью point, который имеет тип:

<class 'pythreejs.pythreejs.Mesh'>

Мне нужно выполнить:

scene.children = list(scene.children) + [point]

Обычно я выполнял:

scene.children.append(point)

Однако, хотя эти два подхода добавляют point, только первый фактически обновляет список и выдает ожидаемый результат (то есть воксели в сетке). Почему?

Полный код можно найти здесь.


person nluigi    schedule 10.09.2015    source источник
comment
Нет, append должно работать. Если это не так, происходит что-то очень странное, и вам нужно опубликовать минимально воспроизводимый пример.   -  person Kevin    schedule 10.09.2015
comment
Я понимаю, к сожалению, у меня возникла небольшая проблема с созданием минимального примера, в котором возникает эта проблема :(... В данный момент пытаюсь выяснить, делаю ли я что-то глупое, что вызывает эту проблему   -  person nluigi    schedule 10.09.2015
comment
Единственное объяснение, которое я могу придумать, это то, что scene.children на самом деле не список, это какой-то другой класс, который реализует append, но ведет себя не так, как список. Если вы поместите print(type(scene.children)) в строку, непосредственно предшествующую scene.children.append(point), что получится на выходе?   -  person Kevin    schedule 10.09.2015
comment
@Kevin - это возвращает <type 'list'>; обратите внимание, что scene - это экземпляр класса Scene, а children - это список   -  person nluigi    schedule 10.09.2015
comment
Как вы проверяете содержимое scene.children, чтобы увидеть, присутствует ли внутри него point? Вы проверяете сразу после строки append или через какое-то время?   -  person Kevin    schedule 10.09.2015
comment
Как вы запускаете этот код?   -  person pacholik    schedule 10.09.2015
comment
@pacholik — это файл блокнота ipython   -  person nluigi    schedule 10.09.2015
comment
@Kevin - как выясняется, point фактически добавляется, поскольку point in scene.children возвращает True после добавления. Однако результат таков, что в случае scene.children = list(scene.children) + [point] результат соответствует ожидаемому (воксели добавляются в сетку), а в случае scene.children.append(point) — нет. Это действительно сбивает с толку :(...   -  person nluigi    schedule 10.09.2015
comment
Может быть проблема с обменом ссылками. Если существуют две переменные, которые ссылаются на один и тот же список, то appending изменит содержимое списка обеих переменных. Но если вы сделаете scene.children = some_new_value, то scene.children будет указывать на новый список, а другая переменная по-прежнему будет указывать на старый.   -  person Kevin    schedule 10.09.2015
comment
Может ли быть так, что scene.children — это свойство, которое возвращает копию списка, используемого внутри? Если у вас нет исходного кода или документации, попробуйте print(scene.children is scene.children)   -  person Niklas R    schedule 10.09.2015


Ответы (2)


Я предполагаю, что ваша проблема связана с тем, что children является property (или другим дескриптором), а не простым атрибутом экземпляра Scene, с которым вы взаимодействуете. Вы можете получить список дочерних элементов или назначить атрибуту новый список дочерних элементов, но списки, с которыми вы имеете дело, на самом деле не являются тем способом, которым класс отслеживает своих дочерних элементов внутри себя. Если вы измените список, полученный от scene.children, изменения не отразятся в классе.

Один из способов проверить это — сохранить список из scene.children несколько раз в разных переменных и посмотреть, являются ли они одним и тем же списком или нет. Пытаться:

a = scene.children
b = scene.children
c = scene.children

print(id(a), id(b), id(c))

Я подозреваю, что вы получите разные id для каждого списка.

Вот класс, который демонстрирует ту же проблему, которую вы видите:

class Test(object):
    def __init__(self, values=()):
        self._values = list(values)

    @property
    def values(self):
        return list(self._values)

    @values.setter
    def values(self, new_values):
        self._values = list(new_values)

Каждый раз, когда вы проверяете свойство values, вы получаете новый (скопированный) список.

Я не думаю, что есть исправление, которое принципиально отличается от того, что вы нашли работающим. Вы можете немного упростить работу, используя:

scene.children += [point]

Из-за того, как работает оператор += в Python, он расширяет список, а затем переназначает его обратно на scene.children (a += b эквивалентен a = a.__iadd__(b), если существует метод __iadd__).

person Blckknght    schedule 10.09.2015
comment
Предлагаемый оператор печати возвращает один и тот же id для a, b и c. Кроме того, scene.children+=[point] не дает того же результата, что и scene.children=list(scene.children)+[point]. Однако вы, вероятно, правы в том, что scene.children — это не простой список. - person nluigi; 10.09.2015
comment
На самом деле по исходному коду: github.com/jovyan/pythreejs/ blob/master/pythreejs/, у автора кода, по-видимому, также возникают проблемы с получением списка экземпляров: TODO: figure out how to get a list of instances of Object3d - person nluigi; 10.09.2015
comment
Ах, ну, возможно, он каким-то образом повторно использует объект списка. Как именно ведет себя версия +=? Это ничего не меняет в объекте scene? Работает ли пропуск вызова list в вашем коде и просто выполнение scene.children = scene.children + [point]? - person Blckknght; 10.09.2015
comment
Метод +=, как и метод .append(), кажется, добавляет point в список, однако воксель не добавляется к сцене при двойном щелчке. Пропуск вызова list работает. - person nluigi; 10.09.2015

Согласно этой проблеме, оказывается, что это traitlets проблема. Изменение элементов self.children не вызывает уведомление о событии, если не определен новый список.

person nluigi    schedule 13.09.2015