Python classes--Need explanation - python

I have a general question on the class definition and its use..THe below code from one of the book works fine but I have a general questions.
Here we have defined a class Point and creating 2 instance Point1 & Point2. When calculating the distance for point2, how can we pass the point1 object?
Isn't point1 the point object, whereas the other_point is reprented as a variable.
Im little confused.
Code:
import math
class Point:
def move(self, x, y):
self.x = x
self.y = y
def reset(self):
self.move(0, 0)
def calculate_distance(self, other_point):
print("Inside calculating distance")
return math.sqrt(
(self.x - other_point.x)**2 +
(self.y - other_point.y)**2)
point1 = Point()
point2 = Point()
point1.reset()
point2.move(5,0)
print(point2.calculate_distance(point1))

When you create a Point object, several things happen.
point1 = Point()
point2 = Point()
One of the things that happens is that any methods belonging to the Point class are bound. What this means is that one of the arguments in the method is fixed, so that it always refers to the instance created. Let's look at the definition of calculate_distance.
def calculate_distance(self, other_point):
print("Inside calculating distance")
return math.sqrt(
(self.x - other_point.x)**2 +
(self.y - other_point.y)**2)
You can probably guess which argument is fixed. When Point() is called and an instance is created, the self parameter of calculate_distnace is fixed so that it always refers to that instance. So whenever you do this:
point1.calculate_distance(x)
You're doing the equivalent of this:
Point.calculate_distance(point1, x)
And whenever you do this:
point2.calculate_distance(point1)
You're doing the equivalent of this:
Point.calculate_distance(point2, point1)

That's what the self variable does. So when you are inside the definition of a class, you can use self to identify the object whose data you are trying to manipulate.
For example, suppose you have a class called human (which has a member variable named age), and every year, you want to increase the age of that human by calling the increment_age function. Then, you could write the following code:
class Human:
def __init__(self):
self.age = 0
def increment_age(self):
self.age += 1
>>> h = Human()
>>> print h.age
0
>>> h.increment_age()
>>> print h.age
1
So you see, by calling self, you are referring to the object itself. In your example, this would translate to self referring to point1.
Now, suppose that in the Human class, we want to add a function that allows two humans to fight. In this case, one human would have to fight another human (suppose that fighting another human increases your life by one and decreases the other human's life by one). In that case, you could write the following function within the Human class:
def fight(self, other_human):
self.age += 1
other_human.age -= 1
Now:
>>> h1 = Human()
>>> h2 = Human()
>>> h1.age = 5
>>> h2.age = 3
>>> print h1.age
5
>>> print h2.age
3
>>> h1.fight(h2)
>>> print h1.age
6
>>> print h2.age
2
Thus you can see in this example that h2 is the other_human in the fight function.
Hope that helps

Given your code, point2.calculate_distance(point1) calls calculate_distance with the object referred to by point2 as self, and the object referred to by point1 as other_point.
A good way to learn about these sorts of things is to use a visual debugger, and inspect the values in stack frames as the calls are made.

Inside calculate_distance, other_point is the name used to refer to whatever object is passed as an argument.

Related

How to I manipulate a value in a class in Python?

So i'am new into classes in python, and i was struggling a little bit here :(. So I wanted to make a class point which contained coordinates (x, y). First I made a function which set the values and afterwards a function to print the values in a tuple. But then i wanted to manipulate the y coordinate to reflect along the x-axis. But suddenly it started to give me an AttributeError
Input:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def print(self):
i = (self.x, self.y)
print(tuple(i))
def reflect_x(self):
self.y*=-1
p1 = Point(1,4)
p1.print()
p2 = Point(-3,5)
p2.print()
p3 = Point(-3,-5)
p3.reflect_x().print()
Output:
(1, 4)
(-3, 5)
Traceback (most recent call last):
File "/I'd/love/to/keep/this/private/Oef.py", line 22, in <module>
p3.reflect_x().print()
AttributeError: 'NoneType' object has no attribute 'print'
As you can se as long as i don't implement my reflect_x function I don't get an error.
My wish-to output:
(1, -4)
(-3, -5)
(-3, 5)
Thank you in advance <3
As it was already pointed out, reflect_x() returns default value (None), since you didn't put any return there. If you are used to the programming language that endorses chaining methods, you should add return self in functions you wanna use like that.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def print(self):
i = (self.x, self.y)
print(tuple(i))
return self # change here
def reflect_x(self):
self.y*=-1
return self # change here
p1 = Point(1,4)
p1.print()
p2 = Point(-3,5)
p2.print()
p3 = Point(-3,-5)
p3.reflect_x().print()
therefore allowing your methods to be chainable. A nice video about it.
it seems like you are trying to print the state of your p3 Object after calling reflect_x, right?
For that I would suggest to use p3.print() below your last line like you did before. You cannot call print that way, because it is trying to find it as a function of what reflect_x returns, which is None, and not you Class.
Apply it directly to the object:
p3 = Point(-3,-5)
p3.reflect_x()
p3.print()
As opposed to the result of reflect_x(), which doesn't return anything.
Yeah it's quite simple. When you call the p3.reflect_x().print(), it means you call the print() function from the p3.reflect_x() object. But the p3.reflect_x() means the object return from reflect_x() function of p3 object. And here it is None because you dont return any thing from your code. So you just add the return in reflect_x() function and it will work. It will look like this:
def reflect_x(self):
self.y*=-1
return self

Why do we need to return Point object in __add__ function instead of just returning x and y?

I'm studying operator overloading in Python and came accross with this chunk of code. It is not clear to me, why do we return Point(x,y) in add function instead of just returning x and y.
class Point:
def __init__(self, x=0 , y=0):
self.x = x
self.y = y
def __str__(self):
return("({0},{1})" .format(self.x, self.y))
def __add__(self , other):
x = self.x + other.x
y = self.y + other.y
return Point(x, y) // here if we remove Point object and use return(x,y) it does not cause any errors
p1 = Point(1,5)
p2 = Point(2,5)
print(p1 + p2)
The (x,y) syntax creates a tuple object, while Point(x,y) creates an instance of the Point class and sets it's x and y properties.
There is a difference between these two types of python objects. A tuple is a sequence type object, which is formal talk for a list of values. A tuple, by itself, only has the two values, and the methods that apply for that type of collection. You can read more about tuples here: https://docs.python.org/3.3/library/stdtypes.html?highlight=tuple#tuple
On the other hand, while your Point class is still quite simple, can have much additional functionality via other methods. For example, the tuple will probably not have the add() method you are creating in your point class, or it may have another add() method which does something else. Hope this clears this up.

python global variable from within class not updating by class method

I am making a chess playing AI and have a minimax algorithm that currently returns the state of the game after choosing a move: board, white_pieces and black_pieces.
My chess class handles move generation and maintenance of the state. It has some global variables:
__board
__white_pieces
__black_pieces
Which are declared within the class: body but outside of any methods. This should make it a global variable.
At the beginning of the game, these are initialized using a build_board method (not always implemented in order to test the code). I suppose it could be moved to an init() method at this point now that I have tested the code.
def build_board(self):
for i in range(1, 17):
self.__board[self.__white_pieces[i][1]] = i
self.__white_pieces[i][3] = True
self.__board[self.__black_pieces[i][1]] = -1 * i
self.__black_pieces[i][3] = True
for i in range(0, 20):
self.__board[i] = 99
for i in range(100, 120):
self.__board[i] = 99
for i in range(0, 8):
self.__board[20 + 10 * i] = 99
self.__board[20 + 10 * i + 9] = 99
I'm assuming this makes sense because it uses a list which is a mutable type. Sure, no problem. This edits the board.
Then, when I try to make the game take the state of the game returned by minimax and update those global variables, neither of these seem to update it:
def play_move(self,board,white_pieces,black_pieces):
global __board
global __white_pieces
global __black_pieces
__board = board
__white_pieces = white_pieces
__black_pieces = black_pieces
or
def play_move(self,board,white_pieces,black_pieces):
self.__board = board
self.__white_pieces = white_pieces
self.__black_pieces = black_pieces
Am I misunderstanding something about how global variables work within a class? If someone could clear this up for me that would be great. Thanks!
Edit:
I can even make the playmove methods return the self.__board,self.__white_pieces,self.__black_pieces tuple and print out if they are actually the updated move and they are. Within the method, and passing those results out to another method, states that self.__xxxxx has been updated. From outside of that method though, it does not appear to update the global variables.
Variables declared " within the class: body but outside of any methods" become class attributes, not global variables. Class attributes are shared between all instances of the class, so that's probably not what you want here.
Note that unless there's an instance attribute by the same name shadowing it, you can access a class attribute (and mutate it) from an instance, but as soon as you set (not mutate) this attribute on an instance, it will create an instance attribute that will shadow the class attribute:
>>> class Foo(object):
... bar = []
... def add(self, x):
... self.bar.append(x)
... def set(self, bar):
... self.bar = bar
...
>>> f1 = Foo()
>>> f1.bar
[]
>>> id(f1.bar)
140281150134752
>>> f2 = Foo()
>>> f2.bar
[]
>>> id(f2.bar)
140281150134752
>>> f2.bar is f1.bar
True
>>> f1.add(42)
>>> f2.bar
[42]
>>> f1.bar
[42]
>>> f1.set([33])
>>> f1.bar
[33]
>>> f2.bar
[42]
>>> f2.bar is Foo.bar
True
>>> f1.bar is Foo.bar
False
>>>
Given your use case and snippet, I think that you really want your board etc to be instance attributes instead of globals or class attributes.
You mention that:
Each method needs to be able to access the current state of the board and modify it.
but that's exactly the point of having instance attributes: all methods of an object share the object's state (=> attributes).
As a side note, don't prefix your attributes with a double underscore unless you really know what it does and really need it (hint: you might need this perhaps once every 10 years or so...). A single leading underscore is the convention to denote it's an implementation detail and not part of your class API.

Python procedure for changing an object

Like an example
def inc(a):
a += 1
If I want to have an increment function instead or writing var += 1 (Is not the only case, just as example) what I should do?
I know that I can return a + 1 but I want void fucntion.
Is there any ways in python?
The Python data model doesn't really have variables like other languages. It has objects which may be bound to names. So a Python "variable" isn't a memory location like it is in many other languages, it's simply a label on an object. An object may have multiple names, or it may have none. See Facts and myths about Python names and values by SO veteran Ned Batchelder for further information on this topic.
Python integers are immutable objects, so you can't actually increment them. Of course, you can create a new integer object that has a value 1 greater than the object currently named a and bind the name a to that new object.
So what you're asking to do isn't exactly a natural operation in Python. However, you can get close. As others have mentioned, you can sort-of do it by placing a into a mutable container object. Eg,
def inc(lst):
lst[0] += 1
a = 7
b = [a]
inc(b)
print b, a
output
[8] 7
A somewhat more satisfactory approach is to refer to the name via the global() dictionary:
def inc(k):
globals()[k] += 1
a = 7
inc('a')
print a
output
8
However, modifying things via globals() is generally frowned upon, and it's useless if you want to modify a name that's local to a function.
Another option is to define a custom class:
class MutInt(object):
def __init__(self, v):
self.v = v
def __str__(self):
return str(self.v)
def inc(self):
self.v += 1
a = MutInt(7)
print a
a.inc()
print a
output
7
8
But that's still rather ugly (IMHO), and you'd have to define all the methods of int in it to make the class useful.
You can do this by making a global
def add_one():
global a
a += 1
Notice you don't have to pass a into the function. I would highly recommend against doing this, however.
You need the global statement to modify a global variable in python:
def inc(amount=1):
global a
a+=amount
a = 1
inc(2)
print(a)
this will allow the function to override the value of the globally defined variable.
We can't pass immutable type like int as a variable as a reference:How do I pass a variable by reference. But we can create a new class and overwrite this one's self add operation with python's magic class __iadd__.
class CInt(object):
x = 0
def __init__(self, x):
self.x = x
def __add__(self, y):
return CInt(self.x + y)
def __iadd__(self, y):
self.x = self.x + y
def inc(a):
a += 1
a = CInt(2)
print(a.x)
inc(a)
print(a.x)
The result would be:
2
3

In Python, can I bind a variable to a function/expression so that it automatically updates?

Let's say I've got a variable A that is the result of a function/expression F. F in it's turn has a number of other variables in it, let's say X,Y and Z.
Is it possible to bind A to F so that whenever X,Y or Z changes, A will be updated automatically?
What I want to avoid is that everytime X,Y and Z changes, I have to remember to update A explicitly in the code. I also don't want to call the function everytime I want to use the A.
Example (as per requested): I've got the following function:
def calcHits():
return sum(hitDiceRolls,level*modList['con'])
and in my program (outside of the function), I've got a variable called hitPoints (yes, it's a roleplaying game program). Whenever the variables that's used in the function is changed, I want hitPoints to change as well.
The typical way to do this in Python would be to use a class:
class ExpressionBinder:
def __init__(self, f):
self.f = f
self.x = 0
self.y = 0
self.z = 0
#property
def result(self):
return self.f(self.x, self.y, self.z)
You can use it like this:
def f(x, y, z):
return x**3 + y**2 + z
b = ExpressionBinder(f)
b.x = 1
b.y = 2
b.z = 3
print(b.result)
There is no way in Python to automatically rebind a name in global or local scope in response to other names being rebound. However, it should be possible to make a class that can keep track of some values and have a member function that returns the value you called A. And, as #Alok pointed out, you can use property descriptors to make a member name that implicitly calls a function to return its value, so you can hide the function and treat the name like a plain old name.
class Trk(object):
"""Track some values and compute a function if any change"""
def __init__(self, name, fn, **objects_to_track):
def _trk_fn(self):
if any(self.__dict__[x] != self.original_objects[x] for x in self.original_objects):
self.value = self.saved_fn(self.__dict___)
# now that self.value is updated, also update self.original_objects
for x in self.original_objects:
self.original_objects[x] = self.__dict__[x]
return self.value
self.original_objects = objects_to_track # make reference copy
self.__dict__.update(objects_to_track)
self.name = name
self.saved_fn = fn
self.fn = self._trk_fn()
self.value = self.fn()
I'm sorry but I am very tired right now, and I canot finish this example. I didn't test it either. But this shows one way to track values, and if they are different, do something different. You use it like this:
# want to track x, y, z
trk = Trk(x, y, z)
trk.fn() # returns up-to-date value
trk.x = new_value
trk.fn() #detects that trk.x changed and computes new trk.value
If the above works, you can use the property descriptor stuff to bind a name such that an attempt to read a value from the name will call self.fn()
EDIT: Oh, it's important that when self.value is updated, self.original_objects should be updated. I've added code to do that.
And now I'm going to sleep!

Categories

Resources