Name "function name" is not defined - python

I've been attempting to complete one of my subjects for the OOP, but I couldn't manage to figure out what's wrong with the definition of my functions. I'm a newbie in this language, already searched the forums but I couldn't find anything useful.
Error message:
Traceback (most recent call last):
File "/home/main.py", line 44, in
add(x, y)
NameError: name 'add' is not defined
This is the program I wrote.
class Complex(object):
def __init__(self, r = 0, i = 0):
self.r = r
self.i = i
def __str__ (self):
return "%s + i%s" % (self.r, self.i)
def add (self, other):
summ = Complex()
summ.r = self.r + other.r
summ.i = self.i + other.i
return "Sum is %d" % summ
def dif (self, other):
dif = Complex()
dif.r = self.r - other.r
dif.i = self.i - other.i
return dif
def multiply (self, other):
multiply = Complex()
multiply.r = self.r * other.r
multiply.i = self.i * other.i
return "%d" % (-1 * multiply.r * multiply.i)
def divide (self, other):
divide = Complex()
divide.r = self.r / other.r
divide.i = self.i / other.i
return "%d" % (divide.r / divide.i)
x = Complex()
y = Complex()
x.r = int(input("Type in real part of x: \n"))
x.i = int(input("Type in imaginary of x: \n"))
print (x, "\n")
y.r = int(input("Type in real part of y: \n"))
y.i = int(input("Type in imaginary of y: \n"))
print (y, "\n")
add(x, y)

The add() function is a member of the Complex class but you are calling it on its own.
You need to call it like this
x.add(y)
When a function is a member of a class like below:
class Complex(object):
def add (self, other):
# Do stuff
The object the function is called on automatically gets passed as self.
So if you call x.add(y), self will be x and other will be y.

If add is a method of your class Complex, then you must write x.add(y) instead of add(x, y).
I know that it is confusing in Python. You need always to apply the "self" as first argument of a method, although it is not an argument, but the object whose method you call.

Related

Not sure why I'm stuck in a Python stuck recursion loop

The add and mul definitions here are nonsensical because of their dependence on returning self, causing infinite loops. If they create a new distribution using the lambdas then it works fine, as in my own answer below.
I'm just playing around with classes and overriding trying to build a small statistics tool. However, when I run this code I get stuck in a recursion loop inside the __mul__ call which is being run in the n1.pdf call and I cannot figure out why. I think it has something to do with Python lazily executing the __mul__ instead of doing what I kind of 'wanted' (let's say in the language of CS) which was to create a new pointer to the old function call for pdf that is owned by the new pointer to pdf, and then to set the old pointer (the main .pdf pointer) to the new function.
I think this is quite poorly worded so edits extremely welcome if you understand what I'm asking.
import math
import random
class Distribution:
def __init__(self, pdf, cdf):
self.pdf = pdf
self.cdf = cdf
def pdf(self, x):
return self.pdf(x)
def cdf(self, x):
return self.cdf(x)
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
newpdf = lambda x : self.pdf(x) * other
self.pdf = newpdf
newcdf = lambda x : self.cdf(x) * other
self.cdf = newcdf
return self
else:
return NotImplemented
def __add__(self, other):
self.pdf = lambda x : self.pdf(x) + other.pdf(x)
self.cdf = lambda x : self.cdf(x) + other.cdf(x)
return Distribution(self.pdf, self.cdf)
class Normal(Distribution):
def __init__(self, mean, stdev):
self.mean = mean
self.stdev = stdev
def pdf(self, x):
return (1.0 / math.sqrt(2 * math.pi * self.stdev ** 2)) * math.exp(-0.5 * (x - self.mean) ** 2 / self.stdev ** 2)
def cdf(self, x):
return (1 + math.erf((x - self.mean) / math.sqrt(2) / self.stdev)) / 2
def sample(self):
return self.mean + self.stdev * math.sqrt(2) * math.cos(2 * math.pi * random.random())
if __name__ == "__main__":
n1 = Normal(1,2)
n1half = n1 * 0.5
x = n1.pdf(1)
print(x)
p.s. I know that it is no longer a pdf after being multiplied by 0.5, this is not an issue.
class Distribution:
...
def pdf(self, x):
return self.pdf(x)
pdf() calls itself, which calls itself, which calls itself... infinitely.
Same with cdf().
def pdf(self, x):
return self.pdf(x)
def cdf(self, x):
return self.cdf(x)
I assume your intent is to delegate to the attributes. Since they are always assigned, they will be found (assuming you do the lookup on an instance) instead of the class methods (which would straightforwardly be infinite recursion without those attributes); but this in turn means that these class methods are just useless. x.cdf(y), where cdf is a callable instance attribute, just works; there is no need to provide a method as well.
newpdf = lambda x : self.pdf(x) * other
self.pdf = newpdf
I assume your intent is to create a new function that relies upon the existing value of self.pdf. Unfortunately, it doesn't work that way. The problem is that the lambda is late binding. When it executes, that is the time at which it will look up self.pdf... and find itself.
There is a separate problem here, in that you are writing __mul__ and __add__ implementations - that is, the * and + operators, which are supposed to return a new value, and not mutate either operand. (If you wrote a = 3 and b = 4 and then c = a * b, you would be extremely surprised if the values of a or b changed, yes?)
We can solve both problems at once, by simply using the computed pdf and cdf to create a new instance (which we need anyway):
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
newpdf = lambda x : self.pdf(x) * other
newcdf = lambda x : self.cdf(x) * other
return Distribution(newpdf, newcdf)
else:
return NotImplemented
Similarly, __add__ should use local variables, rather than modifying self:
def __add__(self, other):
newpdf = lambda x : self.pdf(x) + other.pdf(x)
newcdf = lambda x : self.cdf(x) + other.cdf(x)
return Distribution(newpdf, newcdf)
Note that implementing these methods also gives you the augmented assignment operators *= and += (albeit by creating a new object and rebinding the name).
Let's test it:
if __name__ == "__main__":
n1 = Normal(1,2)
n1half = n1 * 0.5
print(n1.pdf(1))
print(n1half.pdf(1))
n1 += n1
print(n1.pdf(1))
I get:
>py test.py
0.19947114020071635
0.09973557010035818
0.3989422804014327
Thanks for the help #John and #Tom and #bbbbbb... The problem was trying to return self instead of creating a new distribution. If I change the def'n of mul to
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
def newpdf(x):
return self.pdf(x) * other
def newcdf(x):
return self.cdf(x) * other
return Distribution(newpdf, newcdf)
else:
return NotImplemented
Then this solves this problem

how can i create a class inside a loop

Experiment
class calculator:
class add:
def __init__(self,*arg):
self.arg = arg
def display_new(self):
return sum(self.arg)
class multiply:
def __init__(self,*arg):
self.arg = arg
class multi_num:
def __init__(self,*arg):
self.arg = arg
def nature(self):
sum = 1
for x in arg:
sum=sum*x
return sum
class devide:
def __init__(self,x,y):
self.x = x
self.y = y
def display3(self):
try:
result = self.x/self.y
except ZeroDivisionError:
print("error: divided by zero")
else:
return (result)
calc = calculator()
multiply = calc.multiply(1,2,3,4,5)
multiply.display2()
its not displaying the result. how can I fix this and where am I making mistake ,add and divide is working properly but I don't know why this multiply is not working.
Change arg to self.arg:
def nature(self):
sum = 1
for x in self.arg:
sum=sum*x
return sum

How to get output in Real Number instead of complex number?

I made the following code to solve any quadratic polynomial but I want the final output to be a Real Number (Either a whole number or a fraction) but I get Complex numbers like (3+0j). How to convert them?
Here is the Code:-
import cmath
a = float(raw_input("Enter the Coefficient of x^2 :- "))
b = float(raw_input("Enter the coefficient of x :- "))
c = float(raw_input("Enter the value of constant term or c :- "))
d = ((b*b) - (4*a*c))
if d < 0:
print "There are no Real Roots of this equation"
else:
x1 = (((-b) + cmath.sqrt(float(d))) // 2*a)
x2 = (((-b) - cmath.sqrt(float(d))) // 2*a)
if x1 == x2:
print "x = ", x1
else:
print "x = ", x1, "or", x2
Desired Result:- I want the final result to be a Real Number(both Rational and irrational is allowed including fractions)(Like: 4, 4/3 or something like that).
Simply only print the real part, besides you have to devide by 2a
x1 = (((-b) + cmath.sqrt(float(d))) / (2*a))
x2 = (((-b) - cmath.sqrt(float(d))) / (2*a))
if x1 == x2:
print "x = ", x1.real
else:
print "x = ", x1.real, "or", x2.real
You can use a class like Complex and support imaginary solutions as well.
Code taken from http://hplgit.github.io/primer.html/doc/pub/class/._class-solarized005.html
class Complex(object):
def __init__(self, real, imag=0.0):
self.real = real
self.imag = imag
def __add__(self, other):
return Complex(self.real + other.real,
self.imag + other.imag)
def __sub__(self, other):
return Complex(self.real - other.real,
self.imag - other.imag)
def __mul__(self, other):
return Complex(self.real*other.real - self.imag*other.imag,
self.imag*other.real + self.real*other.imag)
def __div__(self, other):
sr, si, or, oi = self.real, self.imag, \
other.real, other.imag # short forms
r = float(or**2 + oi**2)
return Complex((sr*or+si*oi)/r, (si*or-sr*oi)/r)
def __abs__(self):
return sqrt(self.real**2 + self.imag**2)
def __neg__(self): # defines -c (c is Complex)
return Complex(-self.real, -self.imag)
def __eq__(self, other):
return self.real == other.real and self.imag == other.imag
def __ne__(self, other):
return not self.__eq__(other)
def __str__(self):
return '(%g, %g)' % (self.real, self.imag)
def __repr__(self):
return 'Complex' + str(self)
def __pow__(self, power):
raise NotImplementedError\
('self**power is not yet impl. for Complex')

Class polynomial

I am learning classes in Python, I created a class called polynomial, and am trying to add two polynomials, but always get the following error message
soma.termos[i] = self[i] + other[i] TypeError: 'Polinomio' object
does not support indexing to fix
to fix I created an attribute that is the size of the vector and creates a vector of zeros of size n, but still, the error persists, what is wrong?
class Polinomio:
def __init__ (self, termos = [], n = 0):
self.termos = termos
self.n = [0] * n
def __len__ (self):
return len(self.termos)
def __setitem__ (self, i, x):
self.termos[i] = x
def __add__ (self, other):
soma = Polinomio(n = len(self.termos))
for i in range(len(self.termos)):
soma.termos[i] = self[i] + other[i]
def print (self):
print(self.termos)
p1 = Polinomio([1, 2, 3])
p2 = Polinomio([1, 2, 3])
p2.print()
p3 = Polinomio()
p3 = p1 + p2
You're not using your internal termos property when adding, instead you're trying to index your whole instance which, unsurprisingly, raises an error. Try changing your __add__ method to:
def __add__ (self, other):
soma = Polinomio(n = len(self.termos))
for i in range(len(self.termos)):
soma.termos[i] = self.termos[i] + other[i]
return soma
Or even better:
def __add__ (self, other):
soma = Polinomio(n = len(self.termos))
for i, v in enumerate(self.termos):
soma.termos[i] = v + other[i]
return soma
Also, do not initialize your termos list in your __init__ signature as it will always refer to the same list. Instead, declare it as None and build it as new whenever it's not passed, i.e.:
def __init__ (self, termos = None, n = 0):
self.termos = termos or []
self.n = [0] * n
You should add a method __getitem__:
def __getitem__(self, i):
return self.termos[i]
And also in your __add__ function, you instantiate a Polinomio by saying n = len(self.termos) but your using something called keyword arguments, so it will not actually instantiate a Polinomio with n as len(self.termos, you should instead say Polinomio([], len(self.termos) or implement keyword arguments if you want to use that syntax to intstantiate it.

error when executing same loop

I'm new to Python and I really want to understand why I get this error.
It happens in my findLargest function, while trying to execute the second for loop. The thing is that the second for loop does basically the same thing as the first one, but for some reason I get an error as I try to call on a (class)method. How can this be? Am I not allowed to have 2 for loops for same iterable in the same function?
shapeArea=shape.area()
throws:
TypeError: 'float' object is not callable
The objective of findlargest() is to loop through the set of classes twice, first in order to find the largest value(Area) while the second tries to find if there are other values that are equal.
class Shape(object):
def area(self):
raise AttributeException("Subclasses should override this method.")
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
self.area = (self.base * self.height) / 2
return self.area
def __str__(self):
return "{} with base {} and height {}".format(self.__class__.__name__, self.base, self.height)
def __eq__(self, other):
return type(other) == Triangle and self.base == other.base and self.height == other.height
class ShapeSet:
def __init__(self):
self.shape_list = []
def addShape(self, sh):
if sh not in self.shape_list:
self.shape_list.append(sh)
else:
print ("{} is already existing".format(sh.__str__()))
def __iter__(self):
return (self.shape_list)
def __str__(self):
s = ''
for shape in self.__iter__():
s+= shape.__str__() + "\n"
return s
ss = ShapeSet()
ss.addShape(Triangle(1.2,2.5))
ss.addShape(Triangle(1.4,2.5))
ss.addShape(Triangle(1.3,2.5))
ss.addShape(Triangle(1.5,2.5))
def findLargest(shapes):
maxs = None
maxA = 0.0
for shape in shapes.__iter__():
shapeArea = shape.area()
if shapeArea > maxA or maxs == None:
maxs = shape
maxA = shapeArea
maxTuple = (maxs)
for shape in shapes.__iter__():
shapeArea = shape.area()
With this:
def area(self):
self.area=(self.base*self.height)/2
return self.area
You enter the method and then immediately mask it by assigning a different name to its reference. From then on, self.area refers to that number and you can no longer access that method. Fortunately, the fix is easy: don't save a reference at all.
def area(self):
return self.base * self.height / 2
Python does not separate the names for function/method objects and for other objects. Use unique reference names for any objects you'd like to retain.
I think your problem is here:
class Triangle(Shape):
def __init__(self, base, height):
self.base=base
self.height=height
def area(self):
self.area=(self.base*self.height)/2 ###################### HERE
return self.area
If you have
shape = Triangle(1.5,2.5)
shape.area() # returns float assigned at "HERE" to shape.area
shape.area() # try to call that float assigned in previous step at "HERE"

Categories