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
Related
Say that I have a list of functions: [f1, f2, f3] (in python)
How do I return a single function F:=f3(f2(f1())) (notice that F is of function type). I know that it's possible to do it with .reduce() but I was wondering if there's another way to do it without using libraries.
edit:
Also, notice that the length of the list can be greater than 3
I tried:
def func(list):
i = 1
new_function = filters[0]
while i<=len(filters)-1:
new_function = filters[i](new_function)
i+=1
return new_function
but it doesn't work
The problem in your code is that you pass a function as argument with filters[i](new_function).
I would suggest this recursive solution:
def chain(first, *rest):
return lambda x: first(chain(*rest)(x) if rest else x)
Example use:
def inc(x):
return x + 1
def double(x):
return x * 2
def square(x):
return x * x
f = chain(square, double, inc)
print(f(5)) # ((5+1)*2) ** 2 == 144
I see that in the code you tried, you never actually call the first of your functions. (I also assume that your code starts: def func(filters):
Taking into account that f1() takes no parameter, but the rest take the parameter of the return of the previous function, this should work:
def fit(funcs):
v = funcs[0]()
for f in funcs[1:]:
v = f(v)
return v
def f1():
return 42
def f2(x):
return x
def f3(x):
return x
fs = [f1, f2, f3]
a = lambda:fit(fs)
print(a())
Output: 42
def get_single_func(func_list):
def single_func(*args, **kwargs):
ret = func_list[0](*args, **kwargs)
for func in func_list[1:]:
ret = func(ret)
return ret
return single_func
import math
class Vector:
def __init__(self,x,y):
self.x= x
self.y =y
def add(self,other):
new_x = self.x + other.x
new_y = self.y + other.y
return Vector(new_x,new_y)
def subtract(self,other):
new_x = self.x - other.x
new_y = self.y - other.y
return Vector(new_x,new_y)
def scale(self,factor):
new_x = self.x * factor
new_y = self.y * factor
return Vector(new_x,new_y)
def length(self,other):
r_squared = self.x ** 2 + self.y **2
return Vector(r_squared)
I've been trying to test this code that I was given, how am I able to test this using some numbers so that I am able to learn to understand what each function in this code actually does. I am able to see what it does from looking at the code but I also want to reassure that what I am predicting it to do is actually what it does.
Thank you in advance!
Add a checker for your code at the very end of yor file:
if __name__=="__main__":
vec1 = Vector(0, 0)
vec2 = Vector(2,2)
vec3 = vec1.add(vec2)
print(vec1, vec2, vec3)
#add other tests
You could add an override to a built in function in the Vector class for printing instances in a human readable way.
def __repr__(self):
return 'Vector: ({}, {})'.format(self.x, self.y)
Then you might want to fix the length function. It should only return a number and not another Vector. Additionally, it should return the square root of the sum. For example the vector (3, 4) should have a length of 5, not 25. Also, the length method does not need vector supplied as a param.
Once these are fixed up you can add this to the bottom of the file and run the script in the terminal like so: python vec.py
if __name__ == '__main__':
v1 = Vector(0,0)
v2 = Vector(3,4)
print('v1', v1)
print('v2', v2)
print('v1 + v2', v1.add(v2))
print('v2.length', v2.length())
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.
I created a set of little helper classes for working with degrees and radians. I have included some numpy ufuncs (radians, degrees, sin, cos...) so that I can have deg objects inside of numpy arrays, and perform numpy trig operations (e.g., np.cos(np.array([5*deg, 10*deg, 15*deg]))) on the numpy arrays.
However, I have discovered that when "multiplying" an ndarray by the deg class on the RHS, the numpy object's __mul__ method gets invoked on the array, rather than UnitMeta.__rmul__ leading to raising a TypeError inside of NumberMixin.__new__ as desired. It works correctly (raises an error) when deg is the LHS, e.g. deg * np.array([1]).
Only a portion of the classes is shown below for brevity.
'deg.py'
from numbers import Number
import numpy as np
class UnitMeta(type):
def __mul__(cls, other):
return cls(other)
def __rmul__(cls, other):
'''So can write things like "1 * deg"'''
return cls(other)
class NumberMixin():
def __new__(cls, v):
if not isinstance(v, Number):
raise TypeError('A valid numeric type value is required. {} is not numeric.'.format(type(v)))
return super().__new__()
def __mul__(self, other):
return self._v * other
def __rmul__(self, other):
return self.__mul__(other)
def radians(self): # NOTE: overridden in deg below
return np.radians(self._v)
def degrees(self): # NOTE: overridden in deg below
return np.degrees(self._v)
def sin(self):
return np.sin(self._v)
def cos(self):
return np.cos(self._v)
def tan(self):
return np.tan(self._v)
class deg(NumberMixin, metaclass = UnitMeta):
def __init__(self, d = 0.0):
if isinstance(d, deg):
self._v = d._v
self._deg = d._deg
else:
self._v = np.radians(d)
self._deg = d
def __mul__(self, other):
if isinstance(type(other),UnitMeta):
return NotImplemented
else:
return super().__mul__(other)
def __str__(self):
return str(self._deg) + '°'
def __repr__(self):
return str('deg({})'.format(self._deg))
def __format__(self, spec):
return self._deg.__format__(spec) + '°'
def degrees(self):
return self
def radians(self):
return self._v
if __name__ == '__main__':
try:
print('FAILURE: ', deg * np.array([1]) , ' exception not caught')
except TypeError:
print('SUCCESS: deg * np.array([1]) exception caught.')
try:
print('FAILURE: ', np.array([1]) * deg, ' exception not caught')
except TypeError:
print('SUCCESS: np.array([1]) * deg exception caught.')
Everything else works well.
I want to prevent numpy from doing this so that I can write things more like "normal math", e.g.:
5 * deg
...but have an exception come up, and not get swallowed, when something like this accidentally occurs:
a = np.array([5])
a * deg <- TypeError
Any suggestions? I haven't worked with numpy much as of yet, so apologies if there is an obvious solution.
This is my program (a very simple one):
__author__="soham"
__date__ ="$Aug 12, 2012 4:28:51 PM$"
from math import sqrt
class Point:
x = 0;
y = 0;
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.__dict__ == other.__dict__
def get_dist(self, other):
return sqrt(abs((self.x - other.x)^2 + (self.y - other.y)^2))
def is_rect(self,other,another,yet_another):
return (self.get_dist(other) == another.get_dist(yet_another)) and \
(self.get_dist(another) == other.get_dist(yet_another)) and \
(self.get_dist(yet_another) == other.get_dist(another))
a, b, c, d = Point(4,3),Point(4,9), Point(7,3), Point(7,9)
if a.is_rect(b,c,d):
print "Rectangle."
else:
print "No, not a rectangle!"
This returns no. A similar program written in Java returns the expected answer.
I'm very new to Python. Help!
Exponentiation in Python is **, not ^.
^ in Python is actually the bitwise xor operator.
For exponentation, you can also do pow(x,y) instead of x**y.