nary-, min-, max expression, superclass - python

The task is to introduce three classes:
NaryExpression, MinExpression, and MaxExpression
to complete the UML diagram.
These three classes should support the four methods
__init__(), __str__(), eval(), and occuring_numbers() .
MinExpression.eval() and MaxExpression.eval() should return the
min and max of all the operands, respectively.
class NaryExpression(Expression):
def __init__(self, *operands):
self.operands = operands
def __str__(self) -> str:
return super().__str__()
def eval(self):
return super().eval()
def occuring_numbers():
xoxo = []
class MaxExpression(NaryExpression):
def __init__(self, *operands):
super().__init__(*operands)
def __str__(self) -> str:
return super().__str__()
def eval(self):
return super().eval()
def occuring_numbers():
MaxExpression.eval()
class MinExpression(NaryExpression):
def __init__(self, *operands):
super().__init__(*operands)
def __str__(self) -> str:
return super().__str__()
def eval(self):
return super().eval()
def occuring_numbers():
MinExpression.eval()
if __name__ == '__main__':
One = Constant(1)
Two = Constant(2)
Three = Constant(3)
OneThousand = Constant(1000)
Pi = Constant(round(math.pi, 2))
expr_min = MinExpression(expr1, expr2, expr3)
expr_max = MaxExpression(expr1, expr2, expr3)
for expr in [expr1, expr2, expr3, expr_min, expr_max]:
print(f"{expr} = {expr.eval()}, with numbers {expr.collect_numbers()}")
print()
The coding that is underneath each of the classes I wrote myself, but i don't know how to move forward. I don't know much about classes and can't figure out how im suposed to get the outcome im supposed to get:
min(((3) * (3)) * (3.14), (1000) / (3), (1000) - (2)) = 28.26,
with numbers {2, 3.14, 3, 1000}
max(((3) * (3)) * (3.14), (1000) / (3), (1000) - (2)) = 998,
with numbers {2, 3.14, 3, 1000}
(I have a lot more coding the I can post if this isn't enough to make it make sense).

Related

adding two values from different classes

class PolygonInteriorAngle(object):
def __init__(self, x):
self.x = self
def FindInteriorAngle(self):
degrees = int((x - 2) * 180)
interior = int(degrees / x)
return interior
def PrintInterior(self):
print("interior angle: " + str(self.FindInteriorAngle()))
class PolygonExteriorAngle(object):
def __init__(self, x):
self.x = self
def FindExteriorAngle(self):
exterior = int(360 / x)
return exterior
def PrintExterior(self):
print("exterior angle: " + str(self.FindExteriorAngle()))
class AngleAddition(object):
def __init__(self, x):
self.x = self
def Add(self):
sum = int(interior + exterior)
return sum
def PrintAdd(self):
print("sum of interior and exterior: " + str(self.Add()))
if __name__ == "__main__":
x = int(input("enter: "))
intObj = PolygonInteriorAngle(x)
intObj.FindInteriorAngle()
intObj.PrintInterior()
extObj = PolygonExteriorAngle(x)
extObj.FindExteriorAngle()
extObj.PrintExterior()
addObj = AngleAddition(x)
addObj.Add()
addObj.PrintAdd()
both classes (PolygonInteriorAngle and PolygonExteriorAngle) work fine, they print what they're expected to. what i want to do in the AngleAddition class is to add both of the final values (interior and exterior) that you get from the other two classes. i think it's pretty inefficient putting them in different classes, but that's what my computer science teacher asked me to and i'm not sure how to use a value from another class in a new class. if you do x = 6, you'll get 120 and 60. with AngleAddition i want to print 180.
General comments:
You need to check very carefully the variables in your instance methods. example:
def Add(self):
sum = int(interior + exterior)
return sum
Both interior and exterior are not specified in the instance method arguments.
I agree with User: Tim Roberts's comment. Either you make a base class "angle" and let the "interior/exterior angle" class inherit from the "angle" class, or just use angle class for both interior/exterior angles. It depends on how you want to write the __init__ method for interior/exterior angle classes.
Note that I overrode the magic method to perform the sum of two angles. There are other ways of doing that cause in my way the + operator is being redefined.
Anyways:
class AngleBase:
def __init__(self,angle):
self.angle=angle
def __add__(self,angleobj):
return int(self.angle+angleobj.angle)
class PolygonInteriorAngle(AngleBase):
def __init__(self, side):
degrees = int((side - 2) * 180)
interior = int(degrees / side)
AngleBase.__init__(self,interior)
def FindInteriorAngle(self):
return self.angle
def PrintInterior(self):
print("interior angle: " + str(self.angle))
class PolygonExteriorAngle(AngleBase):
def __init__(self, side):
exterior = int(360 / side)
AngleBase.__init__(self,exterior)
def FindExteriorAngle(self):
return self.angle
def PrintExterior(self):
print("exterior angle: " + str(self.angle))
class AngleAddition:
def __init__(self, x):
pass
def Add(self,interior, exterior):
sum = int(interior + exterior)
return sum
def PrintAdd(self,interior, exterior):
print("sum of interior and exterior: " + str(self.Add(interior, exterior)))
if __name__ == "__main__":
x = int(input("enter: "))
intObj = PolygonInteriorAngle(x)
print(intObj.angle)
intObj.FindInteriorAngle()
intObj.PrintInterior()
extObj = PolygonExteriorAngle(x)
extObj.FindExteriorAngle()
extObj.PrintExterior()
addObj = AngleAddition(x)
addObj.Add(extObj,intObj)
addObj.PrintAdd(extObj,intObj)

How to get 3 inputs in one variables using class method

class Student:
def __init__(self, chemistry, english, math):
self.chemistry = chemistry
self.english = english
self.math = math
def chemistry(self):
return self.chemistry
def english(self):
return self.english
def math(self):
return self.math
def tot(self):
return self.chemistry + self.english + self.math
answer = Student(100,20,30)
answer.tot()
print(answer.tot())
I want to know how to get 3 inputs in one variable. I've tried using "list" but it didn't work.
Explanation
You can use a list or tuple as one variable, for example with a function called add (adds two numbers):
def add(a, b):
return a + b
print(add(3, 8))
def add(nums):
return nums[0] + nums[1]
print(add([3,8])) # List
print(add((3,8))) # Tuple
Code:
For class:
class Student:
def __init__(self, subjects):
self.chemistry = subjects[0]
self.english = subjects[1]
self.math = subjects[2]
# Your Methods, consider renaming them
For initializing class:
answer = Student([100,20,30]) # Using a List
answer = Student((100,20,30)) # Using a Tuple

Clean way to create 'n-digit precision' float child class in Python

I want to create a LimitedPrecisionFloat class that would allow me to store a float with n-digits (I don't want to round the number, just cut the remaining digits). This is how I would like it to behave:
a = LimitedPrecisionFloat(3.45678, ndigits=3)
print(a) # 3.456
a += 0.11199
print(a) # 3.567
a -= 0.567999
print(a) # 3 (or 3.0 or 3.000)
print(a + 1.9999) # 4.999
# same with other arithmetic operators, ie.
print(LimitedPrecisionFloat(1.11111 * 9.0, 3) # 9.999
...
What I currently have is:
def round_down(x:float, ndigits:int):
str_x = str(x)
int_part = str(int(x))
fraction_part = str_x[str_x.find('.') + 1:]
fraction_part = fraction_part[:ndigits]
return float(int_part + '.' + fraction_part)
class LimitedPrecisionFloat(float):
def __new__(cls, x, ndigits:int):
return super(LimitedPrecisionFloat, cls).__new__(cls, round_down(x, ndigits))
def __init__(self, x, ndigits: int):
super().__init__()
self.ndigits = ndigits
def __add__(self, other):
return round_down(float(self) + other, ndigits=self.ndigits)
def __sub__(self, other):
return round_down(float(self) - other, ndigits=self.ndigits)
# other operators
However, this will not work, because whenever __add__ (or any other operator) is called, it returns float and the precision is back in the number. So when I tried doing something like this:
def __add__(self, other):
return LimitedPrecisionFloat(float(self) + other, ndigits=self.ndigits)
But then I got the max recursion depth error.
Because of that, I also don't have a clue on how to implement augmented arithmetic assignments, like __iadd__.

generic Number class refactoring?

I just made a generic number class.
this class is so simple, descriptions are below
from any ordered character list, make number class representing that ordered character list.
create_number_class("01") returns binary number class
create_number_class("0123456789") returns decimal number class
create_number_class("abcdefghij") return decimal number class but representing each digit as a alphabet.
belows is generic number class definition.
I think it is well-made class definition.
are there something needed improvement in that class definition?
thank you all. always.
ex)
ABC_Class = create_number_class("abc")
x = ABC_Class("baa")
y = ABC_Class("bbb")
print(x+y)
#output digits: abc, v: cbb, decimal_v: 22
below is class definition
def create_number_class(alphabet):
class temp(object):
digits = alphabet
def __init__(self, v):
self.v = v
self.decimal_v = self.to_decimal(self)
#staticmethod
def to_decimal(self):
r = 0
for i in range(0, len(self.v)):
r += len(temp.digits)**(len(self.v)-i-1)*(temp.digits.index(self.v[i]))
return r
#classmethod
def from_decimal(cls, decimal_v):
r = []
mod = len(temp.digits)
if decimal_v < mod:
return cls(temp.digits[decimal_v])
while True:
remainder = decimal_v % mod
r.append(remainder)
decimal_v = int((decimal_v - remainder)/ mod)
if decimal_v < mod:
r.append(decimal_v)
break
r = "".join(list(reversed([temp.digits[x] for x in r])))
#r = "".join(list(reversed([str(temp.digits.index(str(x))) for x in r])))
return cls(r)
def __add__(self, other):
return temp.from_decimal(self.decimal_v+other.decimal_v)
def __sub__(self, other):
return temp.from_decimal(self.decimal_v-other.decimal_v)
def __mul__(self, other):
return temp.from_decimal(self.decimal_v*other.decimal_v)
def __floordiv__(self, other):
return temp.from_decimal(self.decimal_v//other.decimal_v)
def __str__(self):
return "digits: {}, v: {}, decimal_v: {}".format(temp.digits, self.v, self.decimal_v)
def convert_to(self, new_class):
return new_class.from_decimal(self.decimal_v)
return temp
below are example
BinClass = create_number_class("01")
DecimalClass = create_number_class("0123456789")
x = BinClass("111")
x = BinClass("1000")
y = BinClass("10")
HexClass = create_number_class('0123456789ABCDEF')
x = HexClass('1')
y = HexClass('AA')
print(x+y)
print(x-y)
print(x*y)
print(x//y)
print(x.convert_to(DecimalClass))
isinstance(x, BinClass)

Strange behavior with generator\iterator

I got a problem with an iterator which I created for some OOP exercises.
Here is the problematic generator:
def shapeIterator(listOfShapes):
print("Generator...")
print(listOfShapes)
listOfShapessoretedbyArea = shape.sortedByArea(listOfShapes)
for shapes in listOfShapessoretedbyArea:
yield str(shapes)
shape.sortedByArea(listOfShapes) is a static method, which need one argument, a list, which is sorted by the calculate area, and returned to the caller.
This method works perfectly in this main function:
if __name__ == '__main__':
rect = rectangle(20, 5)
squa = square(2)
tri = equiTria(2, 5)
circ = circle(2)
pent = pentagon(5)
hexa = hexagon(3)
listOfShapes = [rect, squa, hexa, tri, circ, pent]
listOfShapessoretedbyArea = sorted(listOfShapes, key=lambda x: x.calculate_area())
listOfShapessoretedbyPeri = sorted(listOfShapes, key=lambda x: x.calculate_perimeter())
listOfShapessoretedbyArea2 = shape.sortedByArea(listOfShapes)
listOfShapessoretedbyPeri2 = shape.sortedByPerim(listOfShapes)
iterator = shapeIterator(listOfShapes)
for i in range(6):
sleep(1)
value = next(iterator)
print(value)
print("NOT SORTED")
for shape in listOfShapes:
print(str(shape))
print("\nSORTED BY AREA")
for shape in listOfShapessoretedbyArea:
print(str(shape))
print("\nSORTED BY PERIMETER")
for shape in listOfShapessoretedbyPeri:
print(str(shape))
print("\nSORTED BY AREA v2")
for shape in listOfShapessoretedbyArea2:
print(str(shape))
print("\nSORTED BY PERIMETER v2")
for shape in listOfShapessoretedbyPeri2:
print(str(shape))
but when I move this part:
iterator = shapeIterator(listOfShapes)
for i in range(6):
sleep(1)
value = next(iterator)
print(value)
at the end of the main, like this:
if __name__ == '__main__':
rect = rectangle(20, 5)
squa = square(2)
tri = equiTria(2, 5)
circ = circle(2)
pent = pentagon(5)
hexa = hexagon(3)
listOfShapes = [rect, squa, hexa, tri, circ, pent]
listOfShapessoretedbyArea = sorted(listOfShapes, key=lambda x: x.calculate_area())
listOfShapessoretedbyPeri = sorted(listOfShapes, key=lambda x: x.calculate_perimeter())
listOfShapessoretedbyArea2 = shape.sortedByArea(listOfShapes)
listOfShapessoretedbyPeri2 = shape.sortedByPerim(listOfShapes)
print("NOT SORTED")
for shape in listOfShapes:
print(str(shape))
print("\nSORTED BY AREA")
for shape in listOfShapessoretedbyArea:
print(str(shape))
print("\nSORTED BY PERIMETER")
for shape in listOfShapessoretedbyPeri:
print(str(shape))
print("\nSORTED BY AREA v2")
for shape in listOfShapessoretedbyArea2:
print(str(shape))
print("\nSORTED BY PERIMETER v2")
for shape in listOfShapessoretedbyPeri2:
print(str(shape))
iterator = shapeIterator(listOfShapes)
for i in range(6):
sleep(1)
value = next(iterator)
print(value)
I got this error:
TypeError: sortedByArea() takes 1 positional argument but 2 were given
That's very strange. Trying to do some naive debug, I printed the argument passed at the function sortedByArea() in the second case, and I got effectively two arguments. One is the to string value printed in the last for each statement, and the second is the list itself.
The last to string value is referred to this for each statement:
for shape in listOfShapessoretedbyPeri2:
print(str(shape))
I also tried to change the value of the list, and effectively the value "concatenated" to the argument passed to the shapeIterator function is the last string printed.
If needed here are the classes and import used in the main .py:
from math import pi
from math import sqrt
from time import sleep
class shape():
def calculate_area():
pass
def calculate_perimeter():
pass
def ltarea(self, other):
return self.calculate_area() < other.calculate_area()
def ltperim(self, other):
return self.calculate_perimeter() < other.calculate_perimeter()
def sortedByArea(shapes):
return sorted(shapes, key=lambda x: x.calculate_area())
def sortedByPerim(shapes):
return sorted(shapes, key=lambda x: x.calculate_perimeter())
def nametype(self):
return "shape"
def __str__(self):
return "{0}, area: {1}, perim: {2}".format(self.nametype(),
self.calculate_area(),
self.calculate_perimeter())
class rectangle(shape):
def __init__(self, side1, side2):
self.__side1 = side1
self.__side2 = side2
def calculate_area(self):
return self.__side1 * self.__side2
def calculate_perimeter(self):
return (self.__side1 * 2) + (self.__side2 * 2)
def nametype(self):
return "rectangle"
class square(rectangle):
def __init__(self, side):
self._rectangle__side1 = side
self._rectangle__side2 = side
def nametype(self):
return "square"
class equiTria(shape):
def __init__(self, side, height):
self.__side = side
def calculate_area(self):
self.__height = self.calculate_perimeter() / (2 * sqrt(3))
return (self.__side * self.__height)/2
def calculate_perimeter(self):
return self.__side * 3
def nametype(self):
return "equiTria"
class circle(shape):
def __init__(self, radius):
self.__radius = radius
def calculate_area(self):
return pi * pow(self.__radius, 2)
def calculate_perimeter(self):
return 2 * pi * self.__radius
def nametype(self):
return "circle"
class pentagon(shape):
def __init__(self, side):
self.__side = side
self.__apothem = side * 0.688
def calculate_perimeter(self):
return self.__side * 5
def calculate_area(self):
return (self.calculate_perimeter() * self.__apothem) / 2
def nametype(self):
return "pentagon"
class hexagon(shape):
def __init__(self, side):
self.__side = side
def calculate_area(self):
self.__apothem = self.__side * 0.866
return (self.calculate_perimeter() * self.__apothem) / 2
def calculate_perimeter(self):
return self.__side * 6
def nametype(self):
return "hexagon"
def shapeIterator(listOfShapes):
print("Generator...")
print(listOfShapes)
listOfShapessoretedbyArea = shape.sortedByArea(listOfShapes)
for shapes in listOfShapessoretedbyArea:
yield str(shapes)
You rebind shape in your loops, so it is no longer the class, but one instance.
For example, just above your use of the generator:
for shape in listOfShapessoretedbyPeri2:
print(str(shape))
The variables in the __main__ section are still globals, so that replaced the class used by the generator.
Your options are:
Use a different name for the loop variable; ashape for example.
Use a different name for the class. The Python style guide recommends using CamelCase for class names, so renaming it to Shape would do nicely here.
Put all the code under the if __name__ == '__main__': block in a function, so that variable names like the loop target become locals.
Personally, I'd implement both 2 and 3; avoiding polluting your global namespace is always a good idea, and so is following the almost universally adopted Python style guide; this helps avoid such mistakes in the future.
In addition, if sortedByArea is meant to be a static method, do at least use the #staticmethod decorator. That way it is still useable as a static method even on instances:
class Shape:
# ...
#staticmethod
def sortedByArea(shapes):
return sorted(shapes, key=lambda x: x.calculate_area())
#staticmethod
def sortedByPerim(shapes):
return sorted(shapes, key=lambda x: x.calculate_perimeter())
You reused the shape variable, once for the shape class and once for the loop variable in all your for shape in loops.

Categories

Resources