Python OO, self calling other functions inside class? - python-3.x

I am new to object oriented concepts, and I've tried solving this problem using OO technique. I have solved it using normal programming technique, but I cant get it to work with OO technique.
here is the problem:
https://www.hackerrank.com/challenges/30-nested-logic?utm_campaign=30_days_of_code_continuous&utm_medium=email&utm_source=daily_reminder
What I've tried:
At first, I only called student1.print(). that didnt work so I called parseDate() and calculateFine().
I put self in all the variables in my student class as I fail to truly understand why or how self works.
Apologizes if I incorrectly labeled the title, but I didnt know what else to write, as I am not certain what exactly is the problem in my code.
class getFine():
def __init__ (self,expectedDate,actualDate):
self.expectedDate = expectedDate
self.actualDate = actualDate
def parseDates(self):
self.ya = self.actualDate[0]
self.ma = self.actualDate[1]
self.da = self.actualDate[0]
self.ye = self.expectedDate[0]
self.me = self.expectedDate[1]
self.de = self.expectedDate[2]
def calculateFine(self):
self.fine = 0
if(self.ya>self.ye):
self.fine = 10000
elif self.ya==self.ye:
if(self.ma>self.me):
self.fine = 500 * (self.ma-self.me)
elif(self.ma==self.me) and (self.da>self.de):
self.fine = 15 * (self.da-self.de)
def print(self):
print(self.fine)
def main():
expectedDate = str(input().split(" "))
actualDate = str(input().split(" "))
student1 = getFine(expectedDate, actualDate)
student1.parseDates()
student1.calculateFine()
student1.print()
if __name__ == "__main__":
main()

Your dates are strings, which you then want to subtract from each other. First convert them to integers or floats using something like this:
expectedDate = [int(i) for i in (input().split(" "))]
actualDate = [int(i) for i in (input().split(" "))]
Does this solve your problem?
If you want to only call the getFine.print() function and not the other funtions, you could call these class methods in the getFine.print() method. Since you probably want to separate the year month and date on every function call, you could move that part to the init method
class getFine():
def __init__ (self,expectedDate,actualDate):
self.expectedDate = expectedDate
self.actualDate = actualDate
self.ya = self.actualDate[0]
self.ma = self.actualDate[1]
self.da = self.actualDate[2] # typo here 0 --> 2
self.ye = self.expectedDate[0]
self.me = self.expectedDate[1]
self.de = self.expectedDate[2]
def calculateFine(self):
self.fine = 0
if(self.ya>self.ye):
self.fine = 10000
elif self.ya==self.ye:
if(self.ma>self.me):
self.fine = 500 * (self.ma-self.me)
elif(self.ma==self.me) and (self.da>self.de):
self.fine = 15 * (self.da-self.de)
def print(self):
self.calculateFine()
print(self.fine)
expectedDate = [int(i) for i in (input().split(" "))]
actualDate = [int(i) for i in (input().split(" "))]
student1 = getFine(expectedDate, actualDate)
student1.print()

Related

Create new instance of a given object, using same arguments provided in __init__

Given any iterator, I want to be able to pass this iterator into my function, to return a new iter object that cycles through the given iter infinitely. E.G
class range:
def __init__(self, min, max, step):
self.min = min
self.max = max
self.step = counter
[...] other vars
def __next__(self):
if [.............]
You get the jist. I have a bunch of custom iterators like this, all created with diff args/params. I know I can use vars to get the variables... but how do you distinguish between vars passed into init to create this object, and others vars? Vars returnsmin/max/counter + all the other vars
I have a class like this:
class Cycle:
def __init__(self, iterable):
self.iterable = iterable
def __next__(self):
next_val = next(self.iterable, None)
if next_val is None:
#RESET iterable or create new instance of iterable with same _init_ args used to create it
next_val = next(self.iterable)
return next_val
Intended use:
r = range(0,4,2)
next(r) = 0
next(r) = 2
next(r) = 4
next(r) = stopiterationerror
c = Cycle(r)
next(c) = 0
next(c) = 2
next(c) = 4
next(c) = 0
next(c) = 2
next(c) = 4
.......
I want to keep this cycle function clean and simple if I can. Ideally I would want to simply self.iterable.reset, but it seems like resetting an iterable is not possible in python and I need to create a brand new instance. I know I could pass in *args into cycle, but is it possible to avoid that?
edit currently working:
class Cycle:
def __init__(self, iterable):
self.iterable = iterable
self.copy = copy(iterable)
def __next__(self):
next_val = next(self.iterable, None)
if next_val is None:
self.iterable = self.copy
self.copy = copy(self.copy)
next_val = next(self.iterable, None)
return next_val

How do I return a value from a higher-order function?

guys how can I make it so that calling make_repeater(square, 0)(5) return 5 instead of 25? I'm guessing I would need to change the line "function_successor = h" because then I'm just getting square(5) but not sure what I need to change it to...
square = lambda x: x * x
def compose1(h, g):
"""Return a function f, such that f(x) = h(g(x))."""
def f(x):
return h(g(x))
return f
def make_repeater(h, n):
iterations = 1
function_successor = h
while iterations < n:
function_successor = compose1(h, function_successor)
iterations += 1
return function_successor
it needs to satisfy a bunch of other requirements like:
make_repeater(square, 2)(5) = square(square(5)) = 625
make_repeater(square, 4)(5) = square(square(square(square(5)))) = 152587890625
To achieve that, you have to use the identity function (f(x) = x) as the initial value for function_successor:
def compose1(h, g):
"""Return a function f, such that f(x) = h(g(x))."""
def f(x):
return h(g(x))
return f
IDENTITY_FUNCTION = lambda x: x
def make_repeater(function, n):
function_successor = IDENTITY_FUNCTION
# simplified loop
for i in range(n):
function_successor = compose1(function, function_successor)
return function_successor
if __name__ == "__main__":
square = lambda x: x * x
print(make_repeater(square, 0)(5))
print(make_repeater(square, 2)(5))
print(make_repeater(square, 4)(5))
and the output is
5
625
152587890625
This isn't most optimal for performance though since the identity function (which doesn't do anything useful) is always part of the composed function, so an optimized version would look like this:
def make_repeater(function, n):
if n <= 0:
return IDENTITY_FUNCTION
function_successor = function
for i in range(n - 1):
function_successor = compose1(function, function_successor)
return function_successor

How should define a polynomial in Python 3.7.4

I have the error in the scalar multiply and power method. I tried everything but the result always an error. Which method should fix ???
import math
import numpy as np
from scipy.integrate import quad
class Poly:
def __init__(self,coefficients):
self.coefficients = list(coefficients)
def __call__(self,x):
res = 0
for i in range(len(self.coefficients)):
res += self.coefficients[i] * x**i
return res
def add(self,p):
(coefficients1, coefficients2) = (self.coefficients, p.coefficients)
if(len(coefficients1)>len(coefficients2)):
(coefficients1, coefficients2) = (coefficients2, coefficients1)
coefficients1 = [0]*(len(coefficients2)>len(coefficients1))+coefficients1
coefficients = [coefficients1[i] + coefficients2[i] for i in range(len(coefficients1))]
return Poly(coefficients)
def scalar_multiply(self,n):
np.array(list(coefficients))
return np.array(list(coefficients))**n
def multiply(self,p):
a = self.coefficients
b = p.coefficients
M = len(a)-1
N = len(b)-1
result_coeff = np.zeroes(M+N+1)
for i in range(0,M+1):
for j in range(0,N+1):
result_coeff[i+j] += a[i]*b[j]
return Poly(result_coeff)
def power(self,n):
return np.array(list(coefficients))**n
def diff(self):
for i in range(1, len(self.coefficients)):
self.coefficients[i-1] = i*self.coefficients[i]
del self.coefficients[-1]
def integrate(self):
i = quad(diff,0,x)
print(i[x])
def eval(self):
sum = 0
for i in range(len(self.coefficients)-1,0,-1):
sum+=self.coeffs[i]*(x**i)
return sum(coeff * x**exp for exp, coeff in enumerate(reversed(self.coefficients)))
def print(self):
print(res)
The most immediate problem with the functions you mention is that they try to use a variable coefficients that has not been defined in them. You probably want to be using self.coefficients instead.
That said, there are a lot of other issues in your code (such as returning different types unexpectedly), so just fixing this one problem probably won't lead to your code working correctly, but you can try to address each other issue as you get to them.

Function not working in Python Multiprocessing Pool

This is my code which is working, but a little too slow.
def fsl(Df,p):
NewDf = Df.copy()
if p=='group1':
try:
del NewDf['sl']
del NewDf['Y2030']
except:
pass
selected_clusters = NewDf.loc[(NewDf['Group']==p) & (NewDf['Selected']=='Y'),'Clusters'].tolist()
for i in selected_clusters:
x = 0
surplus = calc_surplus(x,i,p)
while (surplus > 0) and (x < 100000):
x += 100
surplus = calc_surplus(x,i,p)
NewDf.loc[(NewDf['Clusters']==i) & (NewDf['Group']==p),'sl']=x
if p=='group1':
NewDf['sl'] = NewDf['sl'].fillna(0)
return NewDf
I would like the surplus for each selected_cluster to be calculated on parallel to speed up the process.
I moved those codes for parallel run to a new function, and tried to run it on Multiprocessing.Pool like this:
def parallel(i):
x = 0
surplus = calc_surplus(x,i,p)
while (surplus > 0) and (x < 100000):
x += 100
surplus = calc_surplus(x,i,p)
NewDf.loc[(NewDf['Clusters']==i) & (NewDf['Group']==p),'sl']=x
if p=='group1':
NewDf['sl'] = NewDf['sl'].fillna(0)
def fsl(Df,p):
NewDf = Df.copy()
if p=='group1':
try:
del NewDf['sl']
del NewDf['Y2030']
except:
pass
selected_clusters = NewDf.loc[(NewDf['Group']==p) & (NewDf['Selected']=='Y'),'Clusters'].tolist()
if __name__ == '__main__':
with Pool(4) as pool:
pool.map(parallel,[i for i in selected_clusters])
return NewDf
The problem is, the function parallel never runs when the function fsl is called. The column sl is never created. I think the mistake is in pool.map or pool.starmap, but i really can't seem to solve this.
I have seen other threads on Multiprocessing, but most of them don't quite apply to this. What is wrong with my code?

How can I pass different types of parameters (ex: array) into a functional class?

I am trying to learn how to group functions by class. As an example, I tried to code a generalized least squares method to find the equation of a best-fitting line between a set of (x,y) coordinates. For my particular case, I chose a simple line y = x + 5, so slope should be close to 1 and y-intercept should be close to 5. Running my attempt at a coded solution below produces the error TypeError: set_x() takes 1 positional argument but 2 were given, though I am trying to pass an array of x-points. How can I circumvent this error?
import numpy as np
from scipy.optimize import minimize
class GeneralizedLeastSquares:
def __init__(self, residuals=None, parameters=None, x=None, y_true=None, y_fit=None, weights=None, method=None):
self.residuals = residuals
self.parameters = parameters
self.x = x
self.y_true = y_true
self.y_fit = y_fit
self.weights = weights
self.method = method
def set_residuals(self, residuals):
self.residuals = residuals
def set_parameters(self, parameters):
self.parameters = parameters
def set_x(self, x):
self.x = x
def set_y_true(self, y_true):
self.y_true = y_true
def set_y_fit(self, y_fit):
self.y_fit = y_fit
def set_weights(self, weights):
self.weights = weights
def set_method(self, method):
self.method = method
def get_residuals(self):
return [(self.y_true[idx] - self.y_fit[idx])**2 for idx in range(len(self.y_true)) if len(self.y_true) == len(self.y_fit) ]
def get_parameters(self):
return self.parameters
def get_x(self):
return self.x
def get_y_true(self):
return self.y_true
def get_y_fit(self):
return [self.parameters[0] * self.x[idx] + self.parameters[1] for idx in range(len(self.x))]
def get_weights(self):
return self.weights
def update_weights(self):
inverse_residuals = [1/self.residuals[idx] for idx in range(len(residuals))]
inverse_residuals_abs = [abs(inverse_residual) for inverse_residual in inverse_residuals]
residual_abs_total = sum(inverse_residuals_abs)
return [inverse_residuals_abs[idx]/residual_abs_total for idx in range(len(inverse_residuals_abs))]
def get_method(self):
return self.method
def get_error_by_residuals(self):
return sum([self.weights[idx] * self.residuals[idx] for idx in range(len(self.residuals))])
def get_error_by_std_mean(self):
return np.std(self.y_true)/np.sqrt(len(self.y_true))
def get_linear_fit(self):
"""
"""
if self.parameters == 'estimate':
slope_init = (self.y_true[-1] - self.y_true[0]) / (self.x[-1] - self.x[0])
b_init = np.mean([self.y_true[-1] - slope_init * self.x[-1], self.y_true[0] - slope_init * self.x[0]])
self.parameters = [slope_init, b_init]
elif not isinstance(self.parameters, (list, np.ndarray)):
raise ValueError("parameters = 'estimate' or [slope, y-intercept]")
meths = ['residuals', 'std of mean']
funcs = [get_error_by_residuals, get_error_by_std_mean]
func = dict(zip(meths, funcs))[self.method]
res = minimize(func, x0=self.parameters, args=(self,), method='Nelder-Mead')
self.parameters = [res.x[0], res.x[1]]
self.y_fit = get_y_fit(self)
self.residuals = get_residuals(self)
self.weights = update_weights(self)
return self.parameters, self.y_fit, self.residuals, self.weights
x = np.linspace(0, 4, 5)
y_true = np.linspace(5, 9, 5) ## using slope=1, y-intercept=5
y_actual = np.array([4.8, 6.2, 7, 8.1, 8.9]) ## test data
GLS = GeneralizedLeastSquares()
GLS.set_x(x)
GLS.set_y_true(y_actual)
GLS.set_weights(np.ones(len(x)))
GLS.set_parameters('estimate')
# GLS.set_parameters([1.2, 4.9])
GLS.set_method('residuals')
results = GLS.get_linear_fit()
print(results)
Your method is not taking an argument. It should be:
def set_x(self, x):
self.x = x
Wrapping properties in get/set methods is a very Java / outdated way of doing things. It is much easier to access the underlying property outside of your class. I.e. rather than: GLS.set_x(12), consider the more Pythonic: GLS.x = 12. This way you don't have to write a get and set method for each property.
Also, it might make more sense for the heavy lifting method of your object, get_linear_fit to be put in the __call__ method. This way, you can run the regression using by just typing GLS() rather than GLS.get_linear_fit()

Resources