How is value assigned to "other" variable in this Python Class? - python

I am doing this Python Edx course, and one of the examples given during introduction to OOP is below. It is a basic question, lot of you will downvote it, but I need to understand this before I move ahead.
I have already searched on Reddit and asked this question on the Edx forums too, but still don't understand it. I have searched this website too for this specific coding example.
Reddit: https://www.reddit.com/r/learnpython/comments/a54y04/explanation_needed_on_a_simple_class_creation/
class Coordinate(object):
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, other):
x_diff_sq = (self.x-other.x)**2
y_diff_sq = (self.y-other.y)**2
return (x_diff_sq + y_diff_sq)**0.5
point1 = Coordinate(1,2)
point2 = Coordinate(3,4)
print(point1.distance(point2))
What I want to understand is that how is "other.x" value determined by the program? Because I am nowhere assigning a value to other.x and other.y.
For self.x and self.y, there is an explicit assignment happening, but there is no such assignment for "other.x" and "other.y". How does the program assign "3" to "other.x" and "4" to "other.y"?

other is just the argument that the method distance is taking. It is implicitly expected that this argument has the x and y attributes. Whether they really exist or are assigned or not is not a concern for the method distance.
How does the program assign "3" to "other.x" and "4" to "other.y"?
Again, as said before, other is just the name of the argument of the function. Lets look at the only place where you call the function:
print(point1.distance(point2))
This is interpreted as self.distance(other), so we can conclude that in this particular case, other is the object point2.
Now, lets go back a few lines to find any reference of point2. Here it is:
point2 = Coordinate(3,4)
When doing Coordinate(3,4) we are invoking the __init__ method of the class. So in that method, we assigning x with 3 and y with 4.
So you are right, there is no explicit assignment for other.x and other.y, but other is not really an object but an argument. So you have to understand which object it is for a specific function call, and that trace where it is being assigned.
Don't let argument names confuse you!
For self.x and self.y, there is an explicit assignment happening
This is not entirely true as well. Yes a few lines back there is an assignment to self but that's just the same name for two different functions' arguments. The code could be written like this and still work the same:
class Coordinate(object):
def __init__(obj, x, y):
obj.x = x
obj.y = y
def distance(one, two):
x_diff_sq = (one.x-two.x)**2
y_diff_sq = (one.y-two.y)**2
return (x_diff_sq + y_diff_sq)**0.5
Here you would say that there is no explicit assignment to one. But it all depends on each call of each function, what arguments it is being passed.

Related

Python: Modifying an object by assigning it to another object

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.
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 =.
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
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.

Self in python Class - I can do it with out it...? [duplicate]

This question already has answers here:
Why do you need explicitly have the "self" argument in a Python method? [duplicate]
(10 answers)
Closed 6 years ago.
Consider this code:
class example(object):
def __init__ (): # No self
test() # No self
def test(x,y): # No self
return x+y
def test1(x,y): # No self
return x-y
print(example.test(10,5))
print(example.test1(10,5))
15
5
This works as expected. I believe I can write a whole program not using self. What am I missing? What is this self; why is it needed in some practical way?
I have read a lot about it - (stack, Python documentation), but I just don't understand why it's needed, since I can obviously create a program without it.
You can perfectly create a program without it. But then you'd be missing one of the key features of classes. If you can do without self, I'd argue you can do without classes and just do something purely with functions :)
Classes allow you to create objects which have a PROPERTY associated to them, and self allows you to access those values. So say you have a square.
g code:
class Square(object):
def __init__ (self, length, height):
self.length = length # THIS square's length, not others
self.height = height # THIS square's height, not other
def print_length_and_height(self):
print(self.length, self.height) # THIS square's length and height
square1 = Square(2,2)
square2 = Square(4,4)
square1.print_length_and_height() # 2 2
square2.print_length_and_height() # 4 4
Now, this example is quite silly, of course, but i think it shows what SELF specifically is for: it refers to the particular instance of an object.
By all means, if you don't see the point to it, just do away with classes and just use functions, there nothing wrong with that.
You haven't utilised a class or object properly. Cutting out the garbage code, your program reduces to:
def test(x,y): #No class
return x+y
def test1(x,y): #No class
return x-y
print(example.test(10,5))
print(example.test1(10,5))
Output:
15
5
Your "class" is no more useful than if you wrapped your program in the nested structures:
if True:
for i in range(1):
...
A proper object will have attributes (data fields) and functions that operate on that data (see below). Your code has an empty object; hence, you have nothing on which to operate, no need for self, and no need for a class at all.
Rather, use a class when you need to encapsulate a data representation and associated operations. Below, I've reused some of your code to make example do some trivial complex number work. There are many extensions and improvements to make in this; I kept it relatively close to your original work.
class example(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
sign = ' + ' if self.b >= 0 else ' - '
return str(self.a) + sign + str(abs(self.b)) + 'i'
def add(self, x):
self.a += x.a
self.b += x.b
def sub(self, x):
self.a -= x.a
self.b -= x.b
complex1 = example(10, 5)
complex2 = example(-3, 2)
complex1.add(complex2)
print(complex1)
complex2.sub(complex1)
print(complex2)
Output:
7 + 7i
-10 - 5i
Are you familiar with Object-Oriented Paradigm?
If you don't you should check it. Python is a Object-Oriented Language and self lets you define your object properties.
An example:
You have a class named Vehicle. A vehicle could be a bike, a car, even a plane. So something you can include is a name and a type.
class Vehicle():
def init(self, name, type): # Constructor
self.name = name
self.type = type
def info(self):
print("I'm a ")
print(self.name)
That's all, now you have a vehicle with name and type. Every instance of Vehicle would have a name and a type different or not and every intance can access its own variables. I'm sorry I can't explain it better. Firstable you need to know Object-Oriented Paradigm knowledge. Please comment my answer if you have doubts & I'll answer you or give a link where it comes explained better.

Difference between Class variables and Instance variables

I have already read many answers here on Stack Exchange like Python - why use "self" in a class?
After reading these answers, I understand that instance variables are unique to each instance of the class while class variables are shared across all instances.
While playing around, I found that this code which gives the output [1]:
class A:
x = []
def add(self):
self.x.append(1)
x = A()
y = A()
x.add()
print "Y's x: ", y.x
However, this code gives 10 as the output, when in my opinion it should be 11:
class A:
x = 10
def add(self):
self.x += 1
x = A()
y = A()
x.add()
print "Y's x: ", y.x
Why A class variable is not updated when I run x.add()? I am not very experienced in programming, so please excuse me.
Class variables are shadowed by instance attribute. This means that when looking up an attribute, Python first looks in the instance, then in the class. Furthermore, setting a variable on an object (e.g. self) always creates an instance variable - it never changes the class variable.
This means that when, in your second example you do:
self.x += 1
which is (in this case, see footnote) equivalent to:
self.x = self.x + 1
what Python does is:
Look up self.x. At that point, self doesn't have the instance attribute x, so the class attribute A.x is found, with the value 10.
The RHS is evaluated, giving the result 11.
This result is assigned to a new instance attribute x of self.
So below that, when you look up x.x, you get this new instance attribute that was created in add(). When looking up y.x, you still get the class attribute. To change the class attribute, you'd have to use A.x += 1 explicitly – the lookup only happens when reading the value of an attribute.
Your first example is a classical gotcha and the reason you shouldn't use class attributes as "default" values for instance attributes. When you call:
self.x.append(1)
there is no assignment to self.x taking place. (Changing the contents of a mutable object, like a list, is not the same as assignment.) Thus, no new instance attribute is added to x that would shadow it, and looking up x.x and y.x later on gives you the same list from the class attribute.
Note: In Python, x += y is not always equivalent to x = x + y. Python allows you to override the in-place operators separately from the normal ones for a type. This mostly makes sense for mutable objects, where the in-place version will directly change the contents without a reassignment of the LHS of the expression. However, immutable objects (such as numbers in your second example) do not override in-place operators. In that case, the statement does get evaluated as a regular addition and a reassignment, explaining the behaviour you see.
(I lifted the above from this SO answer, see there for more details.)

Returning a counter outside of a Python Function

I am trying to build some code and I have defined a function as this to test how a counter works inside of the function:
def errorPrinting(x):
x += 1
return x
I then use the function in some conditional logic where I want the counter to increase if the conditions are met.
x = 1
for row in arcpy.SearchCursor(fc):
if not row.INCLUSION_TYPE or len(row.TYPE.strip()) == 0:
errorPrinting(x)
print x
elif len(row.TYPE) not in range(2,5):
errorPrinting(x)
print x
elif row.INCLUSION_TYPE.upper() not in [y.upper() for y in TableList]:
errorPrinting(x)
print x
I'm still fairly new with using functions, so maybe I am not understanding how to return the value back outside of the function to be used in the next iteration of the for loop. It keeps returning 1 on me. Can anyone show me how to return the x outside of the function after it has been increased by one x+= 1?
Thanks,
Mike
You're not incrementing your global x, you're incrementing the local paramater that also happens to be named x! (Your parameter to errorPrinting could have been named anything. I'm calling it xLocal.)
As you can see here, x isn't incremented by the function.
>>> def inc(xLocal):
... xLocal += 1
... return xLocal
...
>>> x = 4
>>> inc(x)
5
>>> x
4
You need to reassign the value of x to the return value of the function each time. Like this
x = 1
for row in arcpy.SearchCursor(fc):
if not row.INCLUSION_TYPE or len(row.TYPE.strip()) == 0:
x = errorPrinting(x) # <=== here
print x
elif len(row.TYPE) not in range(2,5):
x = errorPrinting(x) # <=== here
print x
elif row.INCLUSION_TYPE.upper() not in [y.upper() for y in TableList]:
x = errorPrinting(x) # <=== here
print x
Integral parameters and other primitives aren't normally passed by reference in Python. (Lists, dicts, etc. are. Modifying lists unintentionally is actually a very common mistake in Python.)
Edit: passing by "reference" and "value" isn't really correct to talk about in Python. See this nice question for more details.
So, using my previous example:
>>> x = 4
>>> x = inc(x)
>>> x
5
Note that if this had been parameter that is passed by reference, like a list, this strategy would have worked.
>>> def incList(xList):
... for i in range(len(xList)):
... xList[i] += 1
...
>>> xList
[1]
>>> incList(xList)
>>> xList
[2]
Note that normal, Pythonic syntax:
for i in xList:
i += 1
would not increment the global value.
Note: If you're looking to keep tabs on a lot of things, I also recommend the logging module that #SB. mentioned. It's super useful and makes debugging large programs a lot easier. You can get time, type of message, etc.
You're bit by scope. You may want to check out this link for a quick primer.
You can do something simple and say x = errorPrinting(x) in all cases you call errorPrinting and get what you want. But I think there are better solutions where you'll learn more.
Consider implementing an error logger object that maintains a count for you. Then you can do logger.errorPrinting() and your instance of logger will manage the counter. You may also want to look into python's built in logging facilities.
Edited for the OP's benefit, since if functions are a new concept, my earlier comments may be a little hard to follow.
I personally think the nicest way to address this issue is to wrap your related code in an object.
Python is heavily based on the concept of objects, which you can think of as grouping data with functions that operate on that data. An object might represent a thing, or in some cases might just be a convenient way to let a few related functions share some data.
Objects are defined as "classes," which define the type of the object, and then you make "instances," each of which are a separate copy of the grouping of data defined in the class.
class MyPrint(object):
def __init__(self):
self.x = 1
def errorPrinting(self):
self.x += 1
return self.x
def myPrint(self):
for row in arcpy.SearchCursor(fc):
if not row.INCLUSION_TYPE or len(row.TYPE.strip()) == 0:
self.errorPrinting()
print self.x
elif len(row.TYPE) not in range(2,5):
self.errorPrinting()
print self.x
elif row.INCLUSION_TYPE.upper() not in [y.upper() for y in TableList]:
self.errorPrinting()
print self.x
p = MyPrint()
p.myPrint()
The functions __init__(self), errorPrinting(self), and myPrint(self), are all called "methods," and they're the operations defined for any object in the class. Calling those functions for one of the class's instance objects automatically sticks a self argument in front of any arguments that contains a reference to the particular instance that the function is called for. self.x refers to a variable that's stored by that instance object, so the functions can share that variable.
What looks like a function call to the class's name:
p = MyPrint()
actually makes a new instance object of class MyPrint, calls MyPrint.__init__(<instance>), where <instance> is the new object, and then assigns the instance to p. Then, calling
p.myprint()
calls MyPrint.myprint(p).
This has a few benefits, in that variables you use this way only last as long as the object is needed, you can have multiple counters for different tasks that are doing the same thing, and scope is all taken care of, plus you're not cluttering up the global namespace or having to pass the value around between your functions.
The simplest fix, though perhaps not the best style:
def errorPrinting():
global x
x += 1
Then convert x=errorPrinting(x) to errorPrinting ()
"global x" makes the function use the x defined globally instead of creating one in the scope of the function.
The other examples are good though. Study all of them.

how to reach to the instance name of a class?

I am a quit new python programer and just for curiosity I want to know how I can reach to the name of instance which is made in the body of main,in the methods of my class . I want to know whether there is a built-in method for classes or I should manage it myself. by the way, I don't want to use lists or Dictionaries. a sample code is following:
class Vec2D(object):
def __init__(self,X, Y):
self.x=X
self.y=Y
print "X coordinate is ",self.x # I want to add it here
print "Y coordinate is ",self.y # and here
if __name__ == '__main__':
mypoint1 = Vec2D(0,1)
mypoint2 = Vec2D(1,2)
It could be considered as a reporting issue...
You can't. A value has no way to know what variable refers to it.
The reason is quite simple; you can have multiple variables refer to the same value:
mypoint3 = mypoint1
Now what name does the instance have?
Well, for starters, you couldn't print the name in __init__() because the object is still being constructed and hasn't been assigned one yet. Even if it were, there's no built-in way to find out what it is in Python.
However it is possible to search for the object after construction in the module's global namespace, which allows you to do things like this:
class Vec2D(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
my_name = '<unknown>'
namespace = globals().copy()
for name,obj in namespace.items():
if obj is self:
my_name = name
break
return "\n".join(["{!r}:",
" X coordinate is {}",
" Y coordinate is {}"]).format(my_name, self.x, self.y)
if __name__ == '__main__':
mypoint1 = Vec2D(0,1)
mypoint2 = Vec2D(1,2)
print mypoint1
print mypoint2
Output:
'mypoint1':
X coordinate is 0
Y coordinate is 1
'mypoint2':
X coordinate is 1
Y coordinate is 2
However this isn't foolproof because if there are two names for the same object, the for search loop will stop as soon as it finds one of them. It could be modified to find them all, I suppose...
As #MartijnPieters said, these's no way to get the variable name of an instance. The names are not kept after interpreting.
But if you do have the special need, I think there're at least 2 approaches for you.
Let's say you have an instance MyInstance of class MyClass, then
str(MyInstance) will give you a string like <__main__.MyClass instance at 0x1044b6f80>, it uniquely indicates MyInstanceduring the runtime. You might want to use it as the name of the instance.
Explicitly define a "name" in the construction of the class, like:
class MyClass:
def __init__(self, name):
self.name = name
. Afterwards you can always get the name by:
MyInstance = MyClass("Instance1")
print MyInstance.name

Categories

Resources