Ensuring instances of the class have unique attributes' values - python

Say i have a class myclass
class myclass:
def __init__(self,x,y,count):
self.x = x
self.y = y
self.count = count
def add(self,count):
self.count += count
How do I prevent two instances of myclass of having the same x and y? When this happens, add() should be called instead of __init__(), which updates the count of an existing myclass() object instead of creating a new one.

Don't use myclass() to create new objects. Define another function that keeps a dictionary of all the objects, and returns a new one if there isn't one already.
all_myclass = {}
def get_myclass(x, y, count):
if (x, y) not in all_myclass:
all_myclass[(x, y)] = myclass(x, y, count)
return all_myclass[(x, y)]
All other code should use get_myclass() instead of myclass().
Note: this assumes that you never change x or y once you create the object.

Related

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.

Printing with no new line with inherited classes

Just learning python and I write simple code along the way.
Overriding the print method in the derived class, can I print both classes in the same line, without changing the base class?
i.e. I want pointA.print() to print
"x = 10 and y = 10 and z = 10"; not x = 10 and y = 10 and z = 10
I know in C# this can be done easily as the WriteLine method starts with a new line (as opposed to ending with it), and so, the Write method does what I expected it to.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def print(self):
print(f"x = {self.x} and y = {self.y}")
class PointWithZAxis(Point):
def __init__(self, x, y, z):
Point.__init__(self, x, y)
self.z = z
def print(self):
Point.print(self)
print(f"and z = {self.z}")
pointA = PointWithZAxis(10, 10, 10)
pointA.print()
Change the print in the parent class to this:
print(f"x = {self.x} and y = {self.y}", end = "")
BTW, it is cleaner in my opinion to overwrite the __str__ method of the classes than to make a print() method. The __str__ method of an object is automatically called when you print the object (like: print(object)). The code then becomes:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"x = {self.x} and y = {self.y}"
class PointWithZAxis(Point):
def __init__(self, x, y, z):
Point.__init__(self, x, y)
self.z = z
def __str__(self):
return super().__str__() + f"and z = {self.z}"
pointA = PointWithZAxis(10, 10, 10)
print(pointA)
You can add end="" to print
print("something", end="")
Adding the end argument to the print function lets you specify the ending of you print statement, so:
print(..., end='')
By default it is set to end='\n', which indicates a new line.
P.S. be careful with naming funcions print as they shadow already existing functions and may cause confusion.
There are a couple other people who are responding with print(..., end=""). This is a quick-fix, sure. But there's something else I'd like to point out.
Since you're a beginner, I'd say you've encountered a good example that teaches why you should think a little about the kind of APIs/interfaces you're designing. Take a step back and ask yourself this question: "Who are the consumers (callers) of def print(self)? And what do they want." The point here is, you can have different callers each wanting a different thing. These problems arise when you're not able to find the common denominator across all that they want. Introducing parameters to the function is one way. In other words: your derived class can send in a additional parameter asking to not append a newline, while those that call from outside of PointWithZAxis.print(..) need not know about this parameter.
Here's an average solution:
class Point:
....
def print(self, include_newline=True):
print(..., end=("\n" if include_newline else ""))
class PointWithZAxis:
def print(self):
Point.print(self, include_newline=False)
print(....)
Here's a good solution (Don't print. How about making the job just to construct a string? Actual printing can be the callers job).
class Point:
....
def get_print_string(self):
return f"x = {self.x} and y = {self.y}"
class PointWithZAxis:
def get_print_string(self):
return super().get_print_string() + f" and z = {self.z}"
# You can then do:
p = PointWithZAxis()
print(p.get_print_string())
Pythonic solution (recommended): Use __str__(..). This is the recommended way of doing what get_print_string() does.This function indirectly gets called when you do print(AnyClass()). Refer to #Bram Dekker's solution.

python - cast a class to a tuple and "autobox" to a for loop

I was wondering, is there a way in which I can cast a class instance to a tuple in the following way:
Say we have a class A with fields x and y, and we have a list l which contains A instances. I would like to do the following if it's possible:
for (x, y) in l:
print x + y #just an example
I was thinking to use __iter__ to return x and y but it didn't quite work. casting to tuple was working fine but it couldn't autobox the objects in l to (x,y) in the for loop
I know it sounds little silly but a friend was challenging me for something he was working on and I just couldn't do it even though it seems doable
Well, you can do it with __iter__:
def __iter__(self):
yield self.x
yield self.y
but I wouldn't recommend it.
If you're sure you want this thing to be treatable as a tuple, you could use a namedtuple class:
import collections
XAndYClass = collections.namedtuple('XAndYClass', 'x y')
xandy = XAndYClass(1, 2)
print xandy.x # prints 1
x, y = xandy # x = 1, y = 2
You can inherit from that to add methods, if you want.
class XAndYWithMethods(XAndYClass):
def method(self):
print 'doing stuff...'

__setitem__() implementation in Python for Point(x,y) class

I'm trying to make a Point class in python. I already have some of the functions, like __ str__ , or __ getitem__ implemented, and it works great.
The only problem I'm facing is that my implementation of the __ setitem__ does not work, the others are doing fine.
Here is my Point class, and the last function is my __ setitem__():
class point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "point(%s,%s)" % (self.x, self.y)
def __getitem__(self, item):
return (self.x, self.y)[item]
def __setitem__(self, x, y):
[self.x, self.y][x] = y
It should work like this:
p = point(2, 3)
p[0] = 1 # sets the x coordinate to 1
p[1] = 10 # sets the y coordinate to 10
Am I even right, should the `setitem() work like this?
Thanks!
Let self.data and only self.data hold the coordinate values.
If self.x and self.y were to also store these values there is a chance self.data and self.x or self.y will not get updated consistently.
Instead, make x and y properties that look up their values from self.data.
class Point(object):
def __init__(self,x=0,y=0):
self.data=[x, y]
def __str__(self):
return "point(%s,%s)"%(self.x,self.y)
def __getitem__(self,item):
return self.data[item]
def __setitem__(self, idx, value):
self.data[idx] = value
#property
def x(self):
return self.data[0]
#property
def y(self):
return self.data[1]
The statement
[self.x, self.y][x]=y
is interesting but problematic. Let pick it apart:
[self.x, self.y] causes Python to build a new list, with values self.x and self.y.
somelist[x]=y causes Python to assign value y to the xth index of somelist. So this new list somelist gets updated. But this has no effect on self.data, self.x or self.y. That is why your original code was not working.
This is pretty old post, but the solution for your problem is very simple:
class point(object):
def __init__(self,x=0,y=0):
self.x=x
self.y=y
def __str__(self):
return "point(%s,%s)"%(self.x,self.y)
def __getitem__(self,item):
return self.__dict__[item]
def __setitem__(self,item,value):
self.__dict__[item] = value
Each class has his own dictionary with all properties and methods created inside the class. So with this you can call:
In [26]: p=point(1,1)
In [27]: print p
point(1,1)
In [28]: p['x']=2
In [29]: print p
point(2,1)
In [30]: p['y']=5
In [31]: print p
point(2,5)
It is more readable then your "index" like reference.
Let's strip this down to the bare minimum:
x, y = 2, 3
[x, y][0] = 1
print(x)
This will print out 2.
Why?
Well, [x, y] is a brand-new list containing two elements. When you do reassign its first member to 1, that just changes the brand-new list, so its first element is now 1 instead of 2. It doesn't turn the number 2 into the number 1.
Since your code is essentially identical to this, it has the same problem. As long as your variables have immutable values, you can't mutate the variables.
You could fix it by doing something like this:
x, y = [2], [3]
[x, y][0][0] = 1
print(x[0])
Now you'll get 1.
Why? Well, [x, y] is a new list with two elements, each of which is a list. You're not replacing its first element with something else, you're replacing the first element of its first element with something else. But its first element is the same list as x, so you're also replacing x's first element with something else.
If this is a bit hard to keep straight in your head… well, that's usually a sign that you're doing something you probably shouldn't be. (Also, the fact that you're using x for a parameter that means "select x or y" and y for a parameter that means "new value" makes it a whole lot more confusing…)
There are many simpler ways to do the same thing:
Use an if/else statement instead of trying to get fancy.
Use a single list instead of two integer values: self.values[x] = y. (That's unutbu's answer.)
Use a dict instead of two integer values: self.values['xy'[x]] = y.
Use setattr(self, 'xy'[x], y).
Use a namedtuple instead of trying to build the same thing yourself.
This works in python 2.6 i guess it works for 2.7 as well
The __setitem__ method accept 3 arguments (self, index, value)
in this case we want to use index as int for retrive the name of the coordinate from __slots__ tuple (check the documentation of __slots__ is really usefull for performance)
remember with __slots__ only x and y attributes are allowed! so:
p = Point()
p.x = 2
print(p.x) # 2.0
p.z = 4 # AttributeError
print(p.z) # AttributeError
This way is faster respect using #property decorator (when you start to have 10000+ instances)
class Point(object):
#property
def x(self):
return self._data[0] # where self._data = [x, y]
...
so this is my tip for you :)
class Point(object):
__slots__ = ('x', 'y') # Coordinates
def __init__(self, x=0, y=0):
'''
You can use the constructor in many ways:
Point() - void arguments
Point(0, 1) - passing two arguments
Point(x=0, y=1) - passing keywords arguments
Point(**{'x': 0, 'y': 1}) - unpacking a dictionary
Point(*[0, 1]) - unpacking a list or a tuple (or a generic iterable)
Point(*Point(0, 1)) - copy constructor (unpack the point itself)
'''
self.x = x
self.y = y
def __setattr__(self, attr, value):
object.__setattr__(self, attr, float(value))
def __getitem__(self, index):
'''
p = Point()
p[0] # is the same as self.x
p[1] # is the same as self.y
'''
return self.__getattribute__(self.__slots__[index])
def __setitem__(self, index, value):
'''
p = Point()
p[0] = 1
p[1] = -1
print(repr(p)) # <Point (1.000000, -1.000000)>
'''
self.__setattr__(self.__slots__[index], value) # converted to float automatically by __setattr__
def __len__(self):
'''
p = Point()
print(len(p)) # 2
'''
return 2
def __iter__(self):
'''
allow you to iterate
p = Point()
for coord in p:
print(coord)
for i in range(len(p)):
print(p[i])
'''
return iter([self.x, self.y])
def __str__(self):
return "(%f, %f)" % (self.x, self.y)
def __repr__(self):
return "<Point %s>" % self
Your may find it a lot easier to use namedtuple for this:
from collections import namedtuple
Point= namedtuple('Point', ['x','y'])
fred = Point (1.0, -1.0)
#Result: Point(x=1.0, y=-1.0)
The main drawback is that you can't poke values into a namedtuple - it's immutable. In most applications that's a feature, not a bug
What's happening in setitem is it builds a temporary list, sets the value, then throws away this list without changing self.x or self.y. Try this for __setitem__:
def __setitem__(self,coord,val):
if coord == 0:
self.x = val
else:
self.y = val
This is quite an abuse of __setitem__, however... I'd advise figuring out a different way of setting the x/y coordinates if possible. Using p.x and p.y is going to be much faster than p[0] and p[1] pretty much no matter how you implement it.
Here's an example:
from collections import namedtuple
Deck = namedtuple('cards',['suits','values'])
class FrenchDeck(object):
deck = [str(i) for i in range(2,11)]+list('JQKA')
suits = "heart clubs spades diamond".split()
def __init__(self):
self.totaldecks = [Deck(each,every) for each in self.suits for every in self.deck]
def __len__(self):
return len(self.totaldecks)
def __getitem__(self,index):
return self.totaldecks[index]
def __setitem__(self,key,value):
self.totaldecks[key] = value
CardDeck = FrenchDeck()
CardDeck[0] = "asdd" # needs`__setitem__()`
print CardDeck[0]
If you don't use the __setitem__(), you will get an error
TypeError: 'FrenchDeck' object does not support item assignment

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