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.
-
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#delimitersJainil Patel– Jainil Patel2019年08月24日 15:17:54 +00:00Commented Aug 24, 2019 at 15:17
-
= is delimeter , not an operator in python.Jainil Patel– Jainil Patel2019年08月24日 15:18:26 +00:00Commented Aug 24, 2019 at 15:18
-
due to immutable classes , as you are saying python is not efficient language.Jainil Patel– Jainil Patel2019年08月24日 15:21:48 +00:00Commented Aug 24, 2019 at 15:21
-
p1 = Point(2,2) is only way, and it is not efficient , as python dont have pointers concept.Jainil Patel– Jainil Patel2019年08月24日 15:25:44 +00:00Commented Aug 24, 2019 at 15:25
4 Answers 4
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 =
.
-
why are all telling = as operator as it is delimeter according to python docs docs.python.org/3/reference/lexical_analysis.html#delimitersJainil Patel– Jainil Patel2019年08月24日 15:37:56 +00:00Commented 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).hiro protagonist– hiro protagonist2019年08月24日 15:54:38 +00:00Commented Aug 24, 2019 at 15:54
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
-
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.AdmiralAdama– AdmiralAdama2019年08月28日 18:49:28 +00:00Commented 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() :)AdmiralAdama– AdmiralAdama2019年08月28日 18:51:08 +00:00Commented 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 ;)AdmiralAdama– AdmiralAdama2019年08月28日 22:07:39 +00:00Commented 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.Jainil Patel– Jainil Patel2019年08月29日 08:13:18 +00:00Commented Aug 29, 2019 at 8:13
-
p1.__init__(3,3) -------->this don't contains overloading concept. it solves the mutability problem.Jainil Patel– Jainil Patel2019年08月29日 08:15:52 +00:00Commented Aug 29, 2019 at 8:15
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
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.
-
I like it, would definitely use after researching a bit more. +1 for unorthodox (imo) yet compact solution.AdmiralAdama– AdmiralAdama2019年08月28日 18:23:28 +00:00Commented Aug 28, 2019 at 18:23
Explore related questions
See similar questions with these tags.