Создание объекта неизменным по отношению к родителю его родителя - PullRequest
0 голосов
/ 28 февраля 2019

Фон

Если определенный пользователем объект имеет неизменяемый объект в качестве атрибута, то использование оператора += для атрибута имеет другой эффект, чем использование его для другой переменной, указывающей на этот атрибут:

class A():
    def __init__(self):
        # Set self.x to be an (immutable) int
        self.x = 5

a = A()

Сейчас a.x == 5

a.x += 1

Теперь a.x указывает на другой объект, а a.x == 6.

x = a.x

Сейчас x иa.x указывают на один и тот же объект: id(x) == id(a.x) and x == a.x == 6

x += 1

Теперь x и a.x указывают на разные объекты, id(x) != id(a.x) and x == 7 and a.x == 6

Мой вопрос

Можно ли реализовать класс B, имеющий атрибут a, такой, чтобы оператор += работал таким же образом на b.a.x?то есть Я хочу следующее поведение :

Требование 1

b = B()

Теперь я хотел бы b.a.x == 5.

Требование 2

b.a.x += 1

Теперь я хотел бы b.a.x == 6.

Требование 3

a = b.a
a.x += 1

Теперь я хотел бы b.a.x == 6не хочу, чтобы увеличение a.x влияло на b.a.x.

Требование 4

a = A()
b.a = a

Теперь мне бы хотелось b.a.x == 5.

Требование 5

x = b.a.x
a = b.a

Теперь я хотел бы x == 5 and a.x == 5 and b.a.x == 5.

Требование 6

x += 1

Теперь я хотел бы x == 6 and a.x == 5 and b.a.x == 5.Я не хочу, чтобы увеличение x влияло на a.x или b.a.x.

Другими словами, я хочу использовать оператор += для воздействия на b.a.x, но только , когда он применяется непосредственно к b.a.x, а не когда он применяется к другому имени, связанному с тем же объектом во время операции.

То, что я пробовал

Простое определение класса B не работает:

class B():
    def __init__:
        self.a = A()

Это не соответствует требованию 3.

Однако, если я изменю b.aбыть свойством, которое возвращает новую копию a, тогда это тоже не работает:

class B()
    def __init__:
        self._a = A()

    @property
    def a(self):
        return copy(_a)

    @a.setter
    def a(self, value)
        self._a = value

Это не соответствует требованию 2.

Я такжепопытался реализовать X как новый класс с __iadd__ методом , который возвратил новый экземпляр класса.Это означало, что x по-прежнему действовал так, как будто он был неизменным при применении +=, но я не мог понять, как выполнить оба требования 2 и 3, описанные выше.

Я работаю в Python 3.

Ответы [ 2 ]

0 голосов
/ 28 февраля 2019

Это требуемое поведение полностью зависит от того, как реализован оператор += для типа типа x.

Например, если x указывает на объект спискавы не получаете желаемого поведения, и если x указывает на объект int, вы получаете такое поведение.

Итак, на самом деле вы ничего не можете или не должны делать со своим классом A или класс B, чтобы получить это поведение.

Все, что вы делаете, чтобы получить это поведение, должно быть сделано в виде x

0 голосов
/ 28 февраля 2019

TL; DR То, что вы спрашиваете, невозможно, и нарушает многие обещания самого языка.

Учитывайте требование 3
Вы создали новый экземпляр B B() (который создал экземпляр A в своем конструкторе), и ссылка b указывает на этот экземпляр.

b.a - это еще одна ссылка, которая указывает на уже созданный экземпляр A.
Этот код x = b.a просто создает ссылку, указывающую на тот же экземпляр A. Любые изменения, внесенные в объект, будут видны через обе ссылки, указывающие на него..
Это имеет смысл, поскольку этот экземпляр A не знает (и не заботится), является ли он членом другого экземпляра.В обоих случаях его поведение должно быть одинаковым.(Это обещание языка Python, объект ведет себя так же, как и на него когда-либо ссылаются).

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