How can i implement __add__ method in parent class? - python

I have object-oriented programming modelling for geometric shapes. I have add method in each classes if i want to add up two geometric shapes but I have defined in each subclass.
How can i implement the add method in the parent class , so that i don't to defined it for every subclasses?
import numpy as np
class Shape(object):
def __repr__(self):
return type(self).__name__
def __str__(self):
return type(self).__name__
class Circle(Shape):
"""
"""
# constructor
def __init__(self, radius):
self.radius = radius
def __add__(self, other):
if type(other) == int:
self.radius = self.radius + other
else:
newRadius = self.radius + other.radius
return Circle(newRadius)
def __radd__(self, other):
return self.__add__(other)
def area(self):
return np.pi * self.radius**2
class Rectangle(Shape):
# constructor
def __init__(self, width,height):
self.width , self.height = width, height
def __add__(self, other):
if type(other) == int:
self.width = self.width + other
self.height = self.height + other
else:
newWidth = self.width + other.width
newHeight = self.Height + other.Height
return Rectangle(newWidth,newHeight)
def __radd__(self, other):
return self.__add__(other)
def area(self):
"""
Function to compute the area of triangle.
"""
return self.width * self.height

This is a strange question, because it doesn't really make sense to add two circles and have the result be a new circle with the sum of the radiuses. You also have odd behaviour for adding with an int because you are changing the state of the object instead of creating a new one, like you do when adding an object.
But there is in fact a way to do this with just one method in the parent class, using some Python-specific features:
cls = self.__class__ is the class of the current object, which can be used to create a new object of the same class, and test if other is the right type.
d = self.__dict__ is a dictionary of the object's attributes.
The **{ ... } unpacking operator allows calling the cls constructor using a dictionary comprehension to compute the arguments.
I've also written a generic __repr__ which shows the state of the object, for conveniently testing examples in the REPL.
Here's an example:
class Shape:
def __add__(self, other):
cls = self.__class__
d = self.__dict__
if isinstance(other, int):
return cls(**{ k: v + other for k, v in d.items() })
elif isinstance(other, cls):
return cls(**{ k: v + other.__dict__[k] for k, v in d.items() })
else:
raise TypeError()
def __radd__(self, other):
return self.__add__(other)
def __repr__(self):
d = self.__dict__
return '{0}({1})'.format(
self.__class__.__name__,
', '.join('{0}={1!r}'.format(k, v) for k, v in d.items())
)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width, self.height = width, height
Examples:
>>> Circle(4) + Circle(5)
Circle(radius=9)
>>> Circle(6) + 2
Circle(radius=8)
>>> 3 + Circle(2)
Circle(radius=5)
>>> Rectangle(2, 3) + Rectangle(4, 5)
Rectangle(width=6, height=8)
>>> Rectangle(2, 3) + 1
Rectangle(width=3, height=4)
>>> 5 + Rectangle(2, 3)
Rectangle(width=7, height=8)
Note that I've changed the behaviour to always return a new object, instead of mutating the existing one.

Related

How to add two instances of my custom class

I want to run this code (must) including the attribute value next to total in the print section. What code should I insert inside the class to do it?
class Random:
def __init__(self, x):
self.x = x
def __add__(self, other):
return self.x + other.x
p1 = Random(2)
p2 = Random(3)
total = p1 + p2
print(total.value)
Return an instance of Random in your __add__ method and add a property with the name value for the class.
class Random:
def __init__(self, x):
self.x = x
def __add__(self, other):
return Random(self.x + other.x)
#property
def value(self):
return self.x
p1 = Random(2)
p2 = Random(3)
total = p1 + p2
print(total.value)
Of course the better option would be to replace the instance attribute x with value. Then there's no need for the property.
class Random:
def __init__(self, x):
self.value = x
def __add__(self, other):
return Random(self.value + other.value)
Make total a Random as well.
class Random:
def __init__(self, value):
self.value = value
def __add__(self, other):
return Random(self.value + other.value)
p1: Random = Random(2)
p2: Random = Random(3)
total: Random = p1 + p2
print(total.value)

TypeError: __init__() missing 2 required positional arguments: 'x' and 'y' in line 22

class base:
def __init__(self,width,length,x,y):
self.__width=width
self.__length=length
self.__x=x
self.__y=y
def area(self):
return self.__width*self.__length
def perimeter(self):
return 2*(self.__width+self.__length)
def x(self):
return self.__x
def y(self):
return self.__y
class circle(base):
def __init__(self,radius,x,y):
super(circle,self).__init__(x,y)
self.radius=radius
def area(self):
return math.pi*pow(self.radius,2)
def perimeter(self):
return 2*math.pi*self.radius
class rectangle(base):
def __init__(self,width,length,x,y):
super(rectangle,self).__init__(width,length,x,y)
# Test function:
cir=circle(3,1,2)
cir.area()
You should only define data intended for all subclasses inheriting from the base. width and length are not universally applicable.
Following super best practices, __init__ should always call super().__init__, and keyword arguments should be used to ensure that any unrecognized arguments are passed up the inheritance chain.
It is usually sufficient to use _-prefixed names for private attributes, rather than the __-prefixed names subject to name wrangling. It may also be simpler to use them directly; you can always replace them with properties later if it becomes necessary.
class Base:
def __init__(self, x, y, **kwargs):
super().__init__(**kwargs)
self._x = x
self._y = y
# Essentially an abstract method
def area(self):
pass
# Essentially an abstract method
def perimeter(self):
pass
#property
def x(self):
return self._x
#property
def y(self):
return self._y
Each subclass then overrides area and perimeter, and can add shape-specific parameters to __init__ as needed.
class Circle(Base):
def __init__(self, radius, **kwargs):
super().__init__(**kwargs)
self.radius = radius
def area(self):
return math.pi * pow(self.radius, 2)
def perimeter(self):
return 2 * math.pi * self.radius
class Rectangle(Base):
def __init__(self, width, length, **kwargs):
super().__init__(**kwargs)
self.width = width
self.length = length
def area(self):
return self.width * self.length
def perimeter(self):
return 2 * (self.width + self.length)
# Test function:
cir = Circle(radius=3, x=1, y=2)
cir.area()

How to perform value check inside class

So the task is to make a universal Vector class to perform add method whatever(str or int) the x,y values are.
So here is the code that i've tried to execute just to check if try,except somehow works inside a class
class Vector():
def __init__(self,x,y):
self.x = x
self.y = y
def __valuecheck__(self):
try:
self.x + "a"
except TypeError:
return str(self.x)
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return "Vector({},{})".format(self.x,self.y)
a = Vector(1,"a")
b = Vector("a",2)
c = a.__add__(b)
print(c)
The expected output is
Vector(1a,a2)
I've tried different variants, defining classic function e.g. def valuecheck(), as well tried adding try,except to add and init method, but none seem to work. Need your help guys, any tip is very appreciated!
Cheers!
I think I have found the answer.
class Vector():
def __init__(self,x,y):
self.x = x
self.y = y
def __valuecheck__(self):
try:
self.x + "a"
except TypeError:
return str(self.x)
def __repr__(self):
return "Vector({},{})".format(self.x,self.y)
def __add__(self, other):
mvbh = str(self.x), str(self.y) # My Vector Before Hand
myVector = ''.join(mvbh)
ovbh = str(other.x), str(other.y) # Other Vector Before Hand
otherVector = ''.join(ovbh)
final = "Vector({}, {})".format(myVector, otherVector) # Change this to create a new vector
print(final)
a = Vector(1,"a")
b = Vector("a",2)
a.__add__(b)
class Vector():
def __init__(self,x,y):
self.x = x
self.y = y
def __valuecheck__(self):
try:
self.x + "a"
except TypeError:
return str(self.x)
def __add__(self, other):
return Vector(str(self.x) + str(other.x), str(self.y) + str(other.y))
def __repr__(self):
return "Vector({},{})".format(self.x,self.y)
a = Vector(1,"a")
b = Vector("a",2)
c = a.__add__(b)
print(c)

Method argument conversion: 'tuple' has no attribute 'attribute'

I have a simple vector class that overloards several arithmetic operators:
class vec2:
x = 0.0
y = 0.0
def __add__(self,other):
self.x = other.x
self.y = other.y
def __mul__(self,scalar):
self.x *= scalar
self.y *= scalar
However, somewhere else I call the method like this:
class foo:
position = vec2()
velocity = vec2()
def update(self,dt):
self.position += self.velocity * dt;
However, once I get to the update function, the interpreter gives an error:
'tuple' object has no attribute 'x'
inside the __add__ function.
Why is "other" in __add__ passed as a tuple, and not a vec2?
The entire code is here.
Return new vectors when using __add__ and __mul__, and handle 'strange' types:
class vec2:
x = 0.0
y = 0.0
def __init__(self, x=0.0, y=0.0):
self.x, self.y = x, y
def __add__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
result = self.__class__(self.x, self.y)
result.x += other.x
result.y += other.y
return result
def __iadd__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
self.x += other.x
self.y += other.y
return self
def __mul__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
result = self.__class__(self.x, self.y)
result.x *= other.x
result.y *= other.y
return result
def __imul__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
self.x *= other.x
self.y *= other.y
return self
To modify the vectors in-place, use __iadd__ and __imul__; these still need to return the new value; this can be self.
Note that this does not handle just passing in a tuple of (x, y) coordinates. If you want to support that usecase, you need to specially handle it:
class foo:
def __init__(self, position=(0.0, 0.0), velocity=(1.0, 1.0)):
self.position = vec2()
self.velocity = vec2(*velocity)
def update(self, dt):
if isinstance(dt, tuple):
dt = vec2(*dt)
self.position += self.velocity * dt;
Note also that you should not really use class attributes for your position and velocity values; I've used instance attributes instead above, and took the opportunity to set both position and velocity to sane values.
Demo:
>>> f = foo()
>>> f.position.x, f.position.y
(0.0, 0.0)
>>> f.update((1, 2))
>>> f.position.x, f.position.y
(1.0, 2.0)

Decorating arithmetic operators | should I be using a metaclass?

I'd like to implement an object, that bounds values within a given range after arithmetic operations have been applied to it. The code below works fine, but I'm pointlessly rewriting the methods. Surely there's a more elegant way of doing this. Is a metaclass the way to go?
def check_range(_operator):
def decorator1(instance,_val):
value = _operator(instance,_val)
if value > instance._upperbound:
value = instance._upperbound
if value < instance._lowerbound:
value = instance._lowerbound
instance.value = value
return Range(value, instance._lowerbound, instance._upperbound)
return decorator1
class Range(object):
'''
however you add, multiply or divide, it will always stay within boundaries
'''
def __init__(self, value, lowerbound, upperbound):
'''
#param lowerbound:
#param upperbound:
'''
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def init(self):
'''
set a random value within bounds
'''
self.value = random.uniform(self._lowerbound, self._upperbound)
def __str__(self):
return self.__repr__()
def __repr__(self):
return "<Range: %s>" % (self.value)
#check_range
def __mul__(self, other):
return self.value * other
#check_range
def __div__(self, other):
return self.value / float(other)
def __truediv__(self, other):
return self.div(other)
#check_range
def __add__(self, other):
return self.value + other
#check_range
def __sub__(self, other):
return self.value - other
It is possible to use a metaclass to apply a decorator to a set of function names, but I don't think that this is the way to go in your case. Applying the decorator in the class body on a function-by-function basis as you've done, with the #decorator syntax, I think is a very good option. (I think you've got a bug in your decorator, BTW: you probably do not want to set instance.value to anything; arithmetic operators usually don't mutate their operands).
Another approach I might use in your situation, kind of avoiding decorators all together, is to do something like this:
import operator
class Range(object):
def __init__(self, value, lowerbound, upperbound):
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def __repr__(self):
return "<Range: %s>" % (self.value)
def _from_value(self, val):
val = max(min(val, self._upperbound), self._lowerbound)
# NOTE: it's nice to use type(self) instead of writing the class
# name explicitly; it then continues to work if you change the
# class name, or use a subclass
return type(self)(val, rng._lowerbound, rng._upperbound)
def _make_binary_method(fn):
# this is NOT a method, just a helper function that is used
# while the class body is being evaluated
def bin_op(self, other):
return self._from_value(fn(self.value, other))
return bin_op
__mul__ = _make_binary_method(operator.mul)
__div__ = _make_binary_method(operator.truediv)
__truediv__ = __div__
__add__ = _make_binary_method(operator.add)
__sub__ = _make_binary_method(operator.sub)
rng = Range(7, 0, 10)
print rng + 5
print rng * 50
print rng - 10
print rng / 100
printing
<Range: 10>
<Range: 10>
<Range: 0>
<Range: 0.07>
I suggest that you do NOT use a metaclass in this circumstance, but here is one way you could. Metaclasses are a useful tool, and if you're interested, it's nice to understand how to use them for when you really need them.
def check_range(fn):
def wrapper(self, other):
value = fn(self, other)
value = max(min(value, self._upperbound), self._lowerbound)
return type(self)(value, self._lowerbound, self._upperbound)
return wrapper
class ApplyDecoratorsType(type):
def __init__(cls, name, bases, attrs):
for decorator, names in attrs.get('_auto_decorate', ()):
for name in names:
fn = attrs.get(name, None)
if fn is not None:
setattr(cls, name, decorator(fn))
class Range(object):
__metaclass__ = ApplyDecoratorsType
_auto_decorate = (
(check_range,
'__mul__ __div__ __truediv__ __add__ __sub__'.split()),
)
def __init__(self, value, lowerbound, upperbound):
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def __repr__(self):
return "<Range: %s>" % (self.value)
def __mul__(self, other):
return self.value * other
def __div__(self, other):
return self.value / float(other)
def __truediv__(self, other):
return self / other
def __add__(self, other):
return self.value + other
def __sub__(self, other):
return self.value - other
As it is wisely said about metaclasses: if you wonder wether you need them, then you don't.
I don't fully understand your problem, but I would create a BoundedValue class, and us only instances of said class into the class you are proposing.
class BoundedValue(object):
default_lower = 0
default_upper = 1
def __init__(self, upper=None, lower=None):
self.upper = upper or BoundedValue.default_upper
self.lower = lower or BoundedValue.default_lower
#property
def val(self):
return self._val
#val.setter
def val(self, value):
assert self.lower <= value <= self.upper
self._val = value
v = BoundedValue()
v.val = 0.5 # Correctly assigns the value 0.5
print v.val # prints 0.5
v.val = 10 # Throws assertion error
Of course you could (and should) change the assertion for the actual behavior you are looking for; also you can change the constructor to include the initialization value. I chose to make it an assignment post-construction via the property val.
Once you have this object, you can create your classes and use BoundedValue instances, instead of floats or ints.

Categories