Python OOP, cannot overwrite instances - python-3.x

I want to make 2 instances with same name like,
a = SomeClass(someAttr1)
a = SomeClass(someAttr2)
so that the new one should overwrite the previous one.
I also tried this:
a = SomeClass(someAttr1)
a = None
a = SomeClass(someAttr2)
I tried this but it doesn't overwrite the previous instance and adds it in itself, is there any way to do it?
Here is the code:
### Do not change the Location or Campus classes. ###
### Location class is the same as in lecture. ###
class Location(object):
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, deltaX, deltaY):
return Location(self.x + deltaX, self.y + deltaY)
def getX(self):
return self.x
def getY(self):
return self.y
def dist_from(self, other):
xDist = self.x - other.x
yDist = self.y - other.y
return (xDist ** 2 + yDist ** 2) ** 0.5
def __eq__(self, other):
return (self.x == other.x and self.y == other.y)
def __str__(self):
return '<' + str(self.x) + ',' + str(self.y) + '>'
class Campus(object):
def __init__(self, center_loc):
self.center_loc = center_loc
def __str__(self):
return str(self.center_loc)
class MITCampus(Campus):
""" A MITCampus is a Campus that contains tents """
tents_list = []
def __init__(self, center_loc, tent_loc=Location(0, 0)):
""" Assumes center_loc and tent_loc are Location objects
Initializes a new Campus centered at location center_loc
with a tent at location tent_loc """
# Your code here
Campus.__init__(self, center_loc)
self.tent_loc = tent_loc
self.tents_list.append(self.tent_loc)
def add_tent(self, new_tent_loc):
""" Assumes new_tent_loc is a Location
Adds new_tent_loc to the campus only if the tent is at least 0.5 distance
away from all other tents already there. Campus is unchanged otherwise.
Returns True if it could add the tent, False otherwise. """
# Your code here
new_tent_flag = True
for loc in self.tents_list:
if loc == new_tent_loc or new_tent_loc.dist_from(loc) < 0.5:
new_tent_flag = False
if new_tent_flag:
self.tents_list.append(new_tent_loc)
return True
else:
return False
def get_tents(self):
""" Returns a list of all tents on the campus. The list should contain
the string representation of the Location of a tent. The list should
be sorted by the x coordinate of the location. """
# Your code here
new_list_sorted = sorted(self.tents_list, key=lambda tent: tent.getX())
str_list = []
for x in new_list_sorted:
str_list.append(x.__str__())
return str_list
Test Cases:
Test: 0
c = MITCampus(Location(1,2))
print(c.add_tent(Location(1,2)))
print(c.add_tent(Location(0,0)))
print(c.add_tent(Location(2,3)))
print(c.add_tent(Location(2,3)))
print(c.get_tents())
Output:
True
False
True
False
['<0,0>', '<1,2>', '<2,3>']
Test: 1
init campus with default tent loc
c = MITCampus(Location(-1,-2))
print(sorted(c.get_tents()))
Output:
['<0,0>','<0,0>', '<1,2>', '<2,3>']
Expected Output:
['<0,0>']
As can be seen that the second instance should overwrite the previous one but instead it is adding it. Is the problem in the code? and how to solve it?

Ok,
Your tents_list attribute is a class atribute, so even when your c object is overwrited the tents_list attribute stays the same.
It's better to make your tents_list an object argument so the tents_list attribute is overwrited too.
def __init__(self, center_loc, tent_loc=Location(0, 0)):
Campus.__init__(self, center_loc)
self.tents_list = [] # <--- Add this
self.tent_loc = tent_loc
self.tents_list.append(self.tent_loc)

The object is overwritten but you defined tents_list as a class variable so all of the instances of MITCampus share this list.
So any new instance will add to that list and that seems to you as not "overwritten".
If you want that "overwriting" behavior, move the tents_list into the __init__ method as self.tents_list. Only then it will be unique for each instance.

Related

Using self in init part of a class in Python

Is there any difference between the following two codes related to initializing a class in Python?
class summation:
def __init__(self, f, s):
self.first = f
self.second = s
self.summ = self.first + self.second
.
.
.
class summation:
def __init__(self, f, s):
self.first = f
self.second = s
self.summ = f + s
.
.
.
If there exists any difference, what is that, and which code is preferable?
Edit: I am going to write an artificial neural network with Python (and Pytorch). In fact, the above two codes are just some examples. In the actual case, I have seen in various resources that when there exists self.input = input in the initialization of a class, in other parts it is used as self.input, not input.
My questions: What are the differences between these two approaches? Why is the use of self.input preferable, in my case?
Example: (from https://docs.dgl.ai/en/latest/tutorials/models/1_gnn/4_rgcn.html#sphx-glr-tutorials-models-1-gnn-4-rgcn-py)
import torch
import torch.nn as nn
import torch.nn.functional as F
from dgl import DGLGraph
import dgl.function as fn
from functools import partial
class RGCNLayer(nn.Module):
def __init__(self, in_feat, out_feat, num_rels, num_bases=-1, bias=None,
activation=None, is_input_layer=False):
super(RGCNLayer, self).__init__()
self.in_feat = in_feat
self.out_feat = out_feat
self.num_rels = num_rels
self.num_bases = num_bases
self.bias = bias
self.activation = activation
self.is_input_layer = is_input_layer
# sanity check
if self.num_bases <= 0 or self.num_bases > self.num_rels:
self.num_bases = self.num_rels
# weight bases in equation (3)
self.weight = nn.Parameter(torch.Tensor(self.num_bases, self.in_feat,
self.out_feat))
if self.num_bases < self.num_rels:
# linear combination coefficients in equation (3)
self.w_comp = nn.Parameter(torch.Tensor(self.num_rels, self.num_bases))
# add bias
if self.bias:
self.bias = nn.Parameter(torch.Tensor(out_feat))
# init trainable parameters
nn.init.xavier_uniform_(self.weight,
gain=nn.init.calculate_gain('relu'))
if self.num_bases < self.num_rels:
nn.init.xavier_uniform_(self.w_comp,
gain=nn.init.calculate_gain('relu'))
if self.bias:
nn.init.xavier_uniform_(self.bias,
gain=nn.init.calculate_gain('relu'))
def forward(self, g):
if self.num_bases < self.num_rels:
# generate all weights from bases (equation (3))
weight = self.weight.view(self.in_feat, self.num_bases, self.out_feat)
weight = torch.matmul(self.w_comp, weight).view(self.num_rels,
self.in_feat, self.out_feat)
else:
weight = self.weight
if self.is_input_layer:
def message_func(edges):
# for input layer, matrix multiply can be converted to be
# an embedding lookup using source node id
embed = weight.view(-1, self.out_feat)
index = edges.data['rel_type'] * self.in_feat + edges.src['id']
return {'msg': embed[index] * edges.data['norm']}
else:
def message_func(edges):
w = weight[edges.data['rel_type']]
msg = torch.bmm(edges.src['h'].unsqueeze(1), w).squeeze()
msg = msg * edges.data['norm']
return {'msg': msg}
def apply_func(nodes):
h = nodes.data['h']
if self.bias:
h = h + self.bias
if self.activation:
h = self.activation(h)
return {'h': h}
g.update_all(message_func, fn.sum(msg='msg', out='h'), apply_func)
No. there is no difference between these two approaches in your case with this level of information. but could they? Yes. they could. if they have some modifications in their setters or getters. later in my answer I'll show you how.
First of all, I prefer using this one:
class summation:
def __init__(self, f, s):
self.first = f
self.second = s
#property
def summ(self):
return self.first+self.second
the above implementation calculates the summation on demand. so when you change self.first or self.second, summ will be calculated automatically. you can access the sum as you did before.
s = summation(1,9)
print(s.summ)
# 10
s.first = 2
s.second = 3
print(s.summ)
# 5
So, How could they be different?
let's implements them as follows. in setters I doubled the inputs to show you how setters can affect the results. it's just an imaginary example and is not exactly what you wrote.
class summation1:
def __init__(self, f, s):
self.first = f
self.second = s
self.summ = self.first + self.second
#property
def first(self):
return self.__first
#first.setter
def first(self,f):
self.__first = f*2
#property
def second(self):
return self.__second
#second.setter
def second(self,s):
self.__second = s*2
class summation2:
def __init__(self, f, s):
self.first = f
self.second = s
self.summ = f + s
#property
def first(self):
return self.__first
#first.setter
def first(self,f):
self.__first = f*2
#property
def second(self):
return self.__second
#second.setter
def second(self,s):
self.__second = s*2
now let's take a look at the outputs:
a = 3
b = 2
s1 = summation1(a,b)
s2 = summation2(a,b)
print(s1.summ)
# 10
print(s2.summ)
# 5
so, if you are not sure what to choose between those two, maybe the first approach is what you need.

How can I alter/expand the following python code to also calculate mg (miligram?)

I want to expand the following code in order to calculate the miligrams too. Can someone tell me how to?
class weight:
__metric = {"g" : 1,
"kg" : 1000,
}
def __init__(self, value, unit = "g"):
self.value = value
self.unit = unit
def convert_to_gram(self):
return self.value * weight._metric[self.unit]
def __add__(self,other):
x = self.convert_to_gram() + other.convert_to_gram()
return weight + (x/weight._metric[self.unit], self.unit)
def __str__(self):
return "{} {}".format (self.value, self.unit)
class weight:
def __init__(self, value, unit = "g"):
"""
This method is initialized when the object is created
"""
self.value = value
self.unit = unit
# The value of kg is 0.001 (1E-3) because your logic multiplies the input value. So mg must be 1/1E-3 = 1000
self._metric = {"g" : 1,
"kg" : 0.001,
"mg" : 1000
}
def convert_to_gram(self):
"""
This method converts a self.value to g, kg or mg based on self.unit
"""
return self.value * self._metric[self.unit]
def __add__(self, other):
"""
The __add__ method is a 'magic' (dunder) method which gets called when we add two numbers using the + operator.
this method calls convert_to_gram() methods from this class object and from 'other' class object as well
it then returns the sum of both convertion results from the two objects
"""
x = self.convert_to_gram() + other.convert_to_gram()
return (x/self._metric[self.unit], self.unit)
def __str__(self):
return "{} {}".format (self.value, self.unit)
w1 = weight(100, 'mg') # create object w1
w2 = weight(50, 'mg') # create object w2
# call convert_to_gram() to convert 100 to mg and save the result in result_w1
result_w1 = w1.convert_to_gram()
print(result_w1)
# call convert_to_gram() to convert 50 to mg and save the result in result_w2
result_w2 = w2.convert_to_gram()
print(result_w2)
print(w1 + w2)
Gives:
100000
50000
(150.0, 'mg')

sigmoid() takes 1 positional argument but 2 were given

Why am I getting this error
sigmoid() takes 1 positional argument but 2 were given
while using function yHat = NN.forward(X)??
class Neural_Networks(object):
def __init__(self):
self.inputLayerSize =2
self.outputLayerSize =1
self.hiddenLayerSize = 3
#weights
self.W1 = np.random.randn(self.inputLayerSize, self.hiddenLayerSize)
self.W2 = np.random.randn(self.hiddenLayerSize, self.outputLayerSize)
def forward(self,X):
#propogates input through network
self.z2 = np.dot(X, self.W1)
self.a2 = self.sigmoid( self.z2 )
self.Z3 = np.dot(self.a2,self.W2)
yHat = self.sigmoid(self.z3)
return yHat
def sigmoid(z):
return 1/(1+np.exp(-z))
You are using it as an instance method so you must include self as the first argument
class Neural_Networks(object):
def __init__(self):
self.inputLayerSize =2
self.outputLayerSize =1
self.hiddenLayerSize = 3
#weights
self.W1 = np.random.randn(self.inputLayerSize, self.hiddenLayerSize)
self.W2 = np.random.randn(self.hiddenLayerSize, self.outputLayerSize)
def forward(self,X):
#propogates input through network
self.z2 = np.dot(X, self.W1)
self.a2 = self.sigmoid( self.z2 )
self.Z3 = np.dot(self.a2,self.W2)
yHat = self.sigmoid(self.z3)
return yHat
def sigmoid(self, z):
return 1/(1+np.exp(-z))
Conversely if you want to use sigmoid as a class method than you'll need to add a #staticmethod decorator to it eg:
#staticmethod
def sigmoid(z):
return 1/(1+np.exp(-z))
Making it a static method is likely the right option since you don't use self in the method.

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()

Unable to implement __lt__ (less than) method to sort a list of class objects

I am trying to use _lt__ method to compare class objects and sort accordingly.But haven't been able to do so far
class Test:
def __init__(self,x):
self.x = x
def __lt__(self,other):
return self.x < other.x
t1 = Test(10)
t2 = Test(12)
lis = [t1,t2]
lis.sort()
print(lis)
I am getting output as
[<__main__.Test object at 0x7fd4c592fb70>, <__main__.Test object at 0x7fd4c592fb38>]
I thought maybe I need to give a string representation to the object.So I did
def __str__(self):
return "{}".format(self.x)
Still I am getting the same output
[<__main__.Test object at 0x7f212594bac8>, <__main__.Test object at 0x7f212594bc50>]
What am I doing wrong?
you are doing almost everything right; the thing is just that objects inside a list are represented by __repr__ and not __str__:
class Test:
def __init__(self,x):
self.x = x
def __lt__(self,other):
return self.x < other.x
def __repr__(self):
return "{}".format(self.x)
t1 = Test(10)
t2 = Test(12)
lis = [t1,t2]
lis.sort()
print(lis) # [10, 12]

Resources