Python: изменение объекта путем назначения его другому объекту

Я новичок в Python (с C++) и понимаю, что, грубо говоря, все переменные (имена) являются ссылками на объекты Python. Некоторые из этих объектов изменяемы (списки), а другие нет (кортежи, хотя вы можете изменить их элементы, если они сами изменяемы).

Для изменяемых объектов я могу изменить их, обратившись к их функциям-модификаторам (таким как .append()) через имена, к которым они привязаны. Например:

myList = [1,2,3,4]
myList.append(5)

Однако я знаю, что простое присвоение myList второму списку просто создает экземпляр этого второго списка и переназначает ему myList; Исходный список [1,2,3,4] все еще существует, пока сборщик мусора не очистит его (или не очистит, если ему будет присвоено другое имя).

МОЙ ВОПРОС:

Допустим, у меня есть класс Point:

class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

p1 = Point(1,1)
p1.x = 2
p1.y = 2

Как заменить p1.x = 2 и p1.y = 2 одной командой, которая просто присваивает мой объект Point(1,1) объекту Point(2,2)? Очевидно, что p1 = Point(2,2) не работает, так как это просто переназначает имя p1 новому и другому объекту Point(2,2) (а это не то, что мне нужно!).

Есть ли встроенный способ сделать это или мне нужно определить дополнительную функцию модификатора в Point:

def changePoint(self, newPoint):
    self.x = newPoint.x
    self.y = newPoint.y

чтобы сделать это одной командой (т.е. через p1.changePoint(Point(2,2)))? В C++ вы часто можете просто использовать неявно определенный перегруженный оператор присваивания класса (operator=) и выполнить это в одной команде:

SimpleNameClass* objectPtr = new SimpleNameClass("Bob");
//Dereferencing objectPtr and assigning new object:
*objectPtr = SimpleNameClass("Jim");

//Now objectPtr still points to (references) the same address in memory,
//but the underlying object is completely different.

В целом кажется утомительным изменять каждый атрибут по отдельности, когда я хочу преобразовать свой объект в новый, особенно если мой объект содержит много атрибутов!

ИЗМЕНИТЬ:

В дополнение к ответу Джайнила оказывается, что мне даже не нужно менять определение init, я могу просто использовать приведенную выше версию. Затем вы можете преобразовать объект Point в другой с помощью одной команды, например:

p1.__init__(2,2) #Replaces p1.x = 2, p1.y = 2

Это работает, так как исходная инициализация занимает 2 аргумента. Таким образом, стандартный ванильный метод инициализации в основном уже позволяет изменять базовый объект в дополнение к его созданию (по крайней мере, в этом случае). Ура.


person AdmiralAdama    schedule 24.08.2019    source источник


Ответы (3)


class Point:
    def __init__(self, *args):
        if(len(args)==2):
            self.x = args[0]
            self.y = args[1]
        elif(len(args)==1):
            self.x=args[0].x
            self.y=args[0].y




p1 = Point(1,1)
p1.x = 2
p1.y = 2
p1.__init__(Point(3,3))
print(p1.x," ",p1.y)

это именно то, что вы хотите, но в стиле python.

в python = не может быть перегружен, и это не оператор в python, это разделитель в python. см. https://docs.python.org/3/reference/lexical_analysis.html#delimiters

person Jainil Patel    schedule 24.08.2019
comment
Очень нравится идея просто использовать для этого метод init, это имеет смысл и не требует дополнительного метода. Таким образом, init является более универсальным и работает не только для создания экземпляра нового объекта Point, но также и тогда, когда вы просто хотите изменить исходный объект на новый. - person AdmiralAdama; 28.08.2019
comment
Кстати, я добавил второй elif() внутри init, теперь он также работает при создании экземпляра пустого объекта Point(), т.е. p2 = Point() :) - person AdmiralAdama; 28.08.2019
comment
На самом деле, я только что поигрался с решением, и оказалось, что вам вообще не нужно менять метод инициализации. Вы можете просто вызвать это так: p1._init_(3,3), а затем объект, на который ссылается p1, будет изменен! Это работает, поскольку исходный метод инициализации также принимает 2 аргумента. Я все еще принимаю ваш ответ, поскольку вы предложили использовать метод init в первую очередь;) - person AdmiralAdama; 29.08.2019
comment
@AdmiralAdama Я думал, вы хотите, чтобы p1=point(3,3), перегрузка = , поэтому я попытался продемонстрировать своего рода перегрузку в python. вы использовали слово OVERLOADING в вопросе, поэтому мой ответ содержит ** args. - person Jainil Patel; 29.08.2019
comment
p1.__init__(3,3) --------›это не содержит концепции перегрузки. это решает проблему изменчивости. - person Jainil Patel; 29.08.2019
comment
Я просто хочу иметь возможность изменять объект, в частности, заменять его новым объектом с помощью одной команды. Не нужно имитировать перегрузку, просто нужно, чтобы это была одна команда (вместо индивидуального изменения каждого атрибута, что утомительно). Я только что упомянул о перегрузке для C++, потому что именно так вы бы сделали это в C++. Спасибо за вашу помощь. - person AdmiralAdama; 29.08.2019
comment
Что не так с предложенным мной изменением? Это делает вашу функцию инициализации более гибкой;) - person AdmiralAdama; 29.08.2019

одним из способов было бы назначить с помощью распаковки кортежа:

p1.x, p1.y = 2, 2

или вы можете реализовать метод установки в своем классе:

def set_xy(self, x, y):
    self.x, self.y = x, y

но создание нового экземпляра (для такого простого класса) может иметь больше смысла:

p1 = Point(2, 2)

в python вы не можете переопределить оператор присваивания =.

person hiro protagonist    schedule 24.08.2019
comment
почему все говорят = как оператор, поскольку это разделитель в соответствии с документами python docs.python .org/3/reference/lexical_analysis.html#delimiters - person Jainil Patel; 24.08.2019
comment
@JainilPatel согласился, что в python оператор присваивания может быть не лучшим выбором слов (хотя он также упоминается в документ). но ваша ссылка указывает на лексический анализ... функционально += явно является оператором (тогда как в вашей ссылке он рассматривается как (лексический) разделитель). - person hiro protagonist; 24.08.2019

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

def assign_to(obj_one, obj_two) -> None:
    fields = obj_one.__dict__  # Grab the field/value dictionary of the object

    for field_name, field_value in fields.items():
        obj_two.__dict__[field_name] = field_value  # Loop over obj_one's fields and assign them to obj_two

Затем его использование:

p1 = Point(1, 2)
p2 = Point(8, 9)

assign_to(p1, p2)

p2.x, p2.y  # To show that they were modified
# Prints (1, 2)

id(p1), id(p2)  # To show that they're both still distinct objects
# Prints (69029648, 69029296)

Однако у этого могут быть недостатки, так как, честно говоря, я никогда раньше не играл с __dict__. Возможно, вы захотите провести дальнейшее исследование, прежде чем слишком сильно полагаться на него.


Честно говоря, я бы просто написал пользовательскую функцию назначения, как показывают другие ответы. Написание дополнительной строки для каждого поля не должно быть слишком большой проблемой; особенно учитывая, что большинству классов, скорее всего, такая функциональность в любом случае не понадобится. Скорее всего, вы просто собираетесь копировать POD.

person Carcigenicate    schedule 24.08.2019
comment
Мне это нравится, обязательно воспользуюсь после изучения немного больше. +1 за неортодоксальное (imo), но компактное решение. - person AdmiralAdama; 28.08.2019