1

I'm new to Python (coming from C++), and understand that roughly speaking, all variables (names) are references to Python objects. Some of these objects are mutable (lists), while others aren't (tuples, although you can change its elements if they themselves are mutable).

For mutable objects, I can modify them by accessing their modifier functions (such as .append()) through the name(s) they're bound to. For example:

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

However, I know that simply assigning myList to a second list just instantiates this second list and reassigns myList to it; The original list [1,2,3,4] still exists, until garbage collection cleans it up (or not if another name is assigned to it).

MY QUESTION:

Lets say I have a Point class:

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

How can I replace p1.x = 2 and p1.y = 2 with a single command that just assigns my Point(1,1) object to a Point(2,2) object? Clearly, p1 = Point(2,2) doesn't work as this just reassigns the p1 name to a new and different Point(2,2) object (which is not what I need!).

Is there a built-in way to do this or do I need to define an additional modifier function in Point:

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

in order to do this in a single command (i.e. via p1.changePoint(Point(2,2)))? In C++ you can often just use a class' implicitly defined overloaded assignment operator (operator=) and accomplish this in a single command:

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.

Overall, it seems tedious to have to change every attribute individually when I want to transform my object into a new one, especially if my object contains many attributes!

EDIT:

Adding to Jainil's answer, it turns out I don't even need to change the definition of init at all, I can just use the above version. Then, you can transform a Point object to another one with a single command, like so:

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

It works since the original init takes to 2 args. So a standard, vanilla init method basically already enables changing the underlying object, in addition to instantiating it (at least in this case). Yipee.

asked Aug 24, 2019 at 14:26
4
  • in python = can't be overloaded and it is not an operator in python, it is delimeter in python. see docs.python.org/3/reference/lexical_analysis.html#delimiters Commented Aug 24, 2019 at 15:17
  • = is delimeter , not an operator in python. Commented Aug 24, 2019 at 15:18
  • due to immutable classes , as you are saying python is not efficient language. Commented Aug 24, 2019 at 15:21
  • p1 = Point(2,2) is only way, and it is not efficient , as python dont have pointers concept. Commented Aug 24, 2019 at 15:25

4 Answers 4

3

one way would be to assign using tuple unpacking:

p1.x, p1.y = 2, 2

or you could implement a setter method in your class:

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

but creating a new instance (for a class this simple) may make more sense:

p1 = Point(2, 2)

in python you can not override the assignment operator =.

answered Aug 24, 2019 at 14:38
2
  • why are all telling = as operator as it is delimeter according to python docs docs.python.org/3/reference/lexical_analysis.html#delimiters Commented Aug 24, 2019 at 15:37
  • @JainilPatel agreed, in python "assignment operator" may not be the best choice of words (although it is also mentioned in the doc). but your link points to the lexical analysis... functionally += is clearly an operator (whereas in your link it is treated as a (lexical) delimiter). Commented Aug 24, 2019 at 15:54
1
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)

it is just what you want , but in python way.

in python = can't be overloaded and it is not an operator in python, it is delimeter in python. see https://docs.python.org/3/reference/lexical_analysis.html#delimiters

answered Aug 24, 2019 at 15:33
7
  • Really like the idea of just using the init method for this, it makes sense and doesn't require an additional method. This way init is more generalized and works not just for instantiating a new Point object but also when you simply want to change the original object to a new one. Commented Aug 28, 2019 at 18:49
  • Btw, I added a 2nd elif() inside init, now it also works when instantiating an empty Point() object, i.e. p2 = Point() :) Commented Aug 28, 2019 at 18:51
  • Actually, I was playing around with the solution just now, and it turns out you don't even have to change the init method at all. You can just call it like this: p1._init_(3,3) and then the object p1 is referencing is modified! It works since the original init method accepts 2 arguments as well. I still accept your answer though since you suggested using the init method in the first place ;) Commented Aug 28, 2019 at 22:07
  • @AdmiralAdama i thought you want p1=point(3,3) ,overloading of = , so i tried to demonstrate a kind of overloading in python. you have used the word "OVERLOADING" in question , that's why my answer contains **args. Commented Aug 29, 2019 at 8:13
  • p1.__init__(3,3) -------->this don't contains overloading concept. it solves the mutability problem. Commented Aug 29, 2019 at 8:15
1
class Point:
 def __init__(self, x=0, y=0):
 self.x = x
 self.y = y
 def change_point(self, new_x, new_y):
 self.x = new_x
 self.y = new_y
answered Aug 24, 2019 at 14:45
1

I'm not sure this is necessarily encouraged, but you can directly modify the __dict__ attribute of the object to modify it. That leads to a solution like:

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

Then its use:

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)

This may have drawbacks though, as honestly I've never played around with __dict__ before. You may want to do further research into it before relying on it too heavily.


I'd honestly just write a custom assigning function as the other answers show. Writing an extra line per field shouldn't be too big of a deal; especially given most classes likely won't need such functionality anyways. You're likely just going to be copying PODs like this.

answered Aug 24, 2019 at 14:49
1
  • I like it, would definitely use after researching a bit more. +1 for unorthodox (imo) yet compact solution. Commented Aug 28, 2019 at 18:23

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.