My question is regarding some code that is part of an Udacity assignment. The following code is not returning any value. I assume that I'm not calling the "scalar" function properly from my "normalized" function. The line norm = self.scalar(scale) returns type none. Can someone give me a pointer?
Code:
import math
from decimal import Decimal, getcontext
getcontext().prec = 10
class Vector(object):
def __init__(self, coordinates):
try:
if not coordinates:
raise ValueError
self.coordinates = tuple([Decimal(x) for x in coordinates])
self.dimension = len(self.coordinates)
except ValueError:
raise ValueError('The coordinates must be nonempty')
except TypeError:
raise TypeError('The coordinates must be an iterable')
def __eq__(self, v):
return self.coordinates == v.coordinates
def scalar(self, c):
new_coordinates = [Decimal(c)*x for x in self.coordinates]
#new_coordinates = []
#n = len(self.coordinates)
#for i in range(n):
# new_coordinates.append(self.coordinates[i] * c)
#print(Vector(new_coordinates))
def magnitude(self):
new_sq = [x**2 for x in self.coordinates]
new_mag = math.sqrt(sum(new_sq))
return (new_mag)
def normalized(self):
magnitude = self.magnitude()
scale = 1/magnitude
print(scale)
norm = self.scalar(scale)
#print(type(norm))
print(norm)
return (norm)
my_vector = Vector([1,2])
Vector.normalized(my_vector)
Python has this cool little trick where it will always return None if not specified. So if you write a function hello world that doesn't return anything you will get None.
for example:
def hello_world():
print('hello world')
result = hello_world()
print(result) # prints nothing cause result==None
You dont have a return statement in your scalar method, so it will always return None.
My guess is that you want to return the object you create in scalar
def scalar(self, c):
new_coordinates = [Decimal(c)*x for x in self.coordinates]
return new_coordinates
Or for brevity
def scalar(self, c):
return [Decimal(c)*x for x in self.coordinates]
The problem is that you're trying to get a value from scalar even though it doesn't return anything. I'm not entirely sure what you're trying to return so you'll have to deal with that yourself.
One notable issue is with your method calling the attribute of the my_vector instance. It's not technically the issue, but it should probably be changed. Your code should be the following.
my_vector = Vector([1,2])
my_vector.normalized()
Related
I need to overload the addition function so that it takes in the first point and the end point as the left and right side of the equation and outputs the equation. This is what my code looks right now. I'm not sure how to involve the line class?
import math
class Point:
'''Class that creates points. Attributes: Eastings and Northings'''
def __init__(self,x,y):
self.eastings = x
self.northings = y
def getCoords(self):
self.coords = (self.eastings,self.northings)
return self.coords
def setCoords(self,other_x,other_y):
self.eastings = float(other_x)
self.northings = float(other_y)
def __str__(self):
return f"{self.eastings},{self.northings}"
def __add__(self,new_point):
pass
#creates a line (new class)
class Line(Point):
'''Class that creates line object based on two points'''
def __init__(self,start,end):
self.start = start #Type:Point (x1,y1)
self.end = end #Type:Point (x2,y2)
self.latitude = abs(self.end.eastings - self.start.eastings)
self.departure = abs(self.end.northings - self.start.northings)
self.distance = math.sqrt((self.latitude)**2 + (self.departure)**2)
self.azimuth = math.degrees(math.atan2(self.departure,self.latitude))
def __getitem__(self,key):
if key == 0:
ans = self.start
elif key == 1:
ans = self.end
else:
print("invalid index")
return ans
#test code
a = Point(0,0)
b = Point(1,1)
c = Point(1,0.5)
line1 = a+b
print((type(line1))
The test code is supposed to print out the type as class line.
There's nothing that says the __add__() method has to return a the same type as the instance — which means you could this:
class Point:
...
def __add__(self, other):
if isinstance(other, Point):
return Line(self, other) # Line from this Point to the other.
else:
raise TypeError(f"Can't add a non-Point to a Point}")
Be careful doing this however, because the Line class will inherit the method (so you probably need to modify its version of it).
Add a self.getCoords() call to your Point.__init__() method.
Add return Line(self, new_point) to your Point.__add__() method.
Testing:
a = Point(0,0)
b = Point(1,1)
c = Point(1,0.5)
line1 = a+b
print(type(line1)) # I have removed a round bracket on the left
Output: <class '__main__.Line'>
In case you want to run a block of code, where a function/method has not one line in it, you have to add a pass to it. Otherwise you will get an error, because the structure requires it. Or you comment the function declaration out.
So I was trying to create an object which basically has a constructor that takes two coordinates, xcoord and ycoord. I further created methods which shift the coordinates and I have to check if the point is valid or not(criteria for validity is if the coordinates are out of a specified range it should return False else True).
Problem:
My class only returns the validity for the initial points, not the shifted points.
What do I need to correct my code?
Code:
class Point:
MaxScreenSize=10
def __init__(self,x,y):
self.xcoord=x
self.ycoord=y
if 0>self.xcoord or self.xcoord>Point.MaxScreenSize or 0>self.ycoord or self.ycoord>Point.MaxScreenSize:
Point.isValidPt=False
else:
Point.isValidPt=True
def translateX(self,shiftX):
self.xcoord=self.xcoord+shiftX
def translateY(self,shiftY):
self.ycoord=self.ycoord+shiftY
Test Code:
I tried my code and it only returns the isValidFunction variable for my initial points (gives me True instead of False for following code)
p=Point(9,2)
p.translateX(20)
p.translateY(10)
p.isValidPt
Your isValidPt is only calculated when the class is instantiated. Instead try something like:
Code:
class Point:
MaxScreenSize = 10
def __init__(self, x, y):
self.xcoord = x
self.ycoord = y
def translateX(self, shiftX):
self.xcoord = self.xcoord + shiftX
def translateY(self, shiftY):
self.ycoord = self.ycoord + shiftY
#property
def isValidPt(self):
return (
0 <= self.xcoord <= Point.MaxScreenSize and
0 <= self.ycoord <= Point.MaxScreenSize
)
Test Code:
p = Point(9, 2)
p.translateX(20)
p.translateY(10)
print(p.isValidPt)
Result:
False
Constructor is used basically for initiate the values. In your case the constructor checks the initial value and set the validate flag. ie, isValidPt.
It will be True for the scope of p object you created. So you have to create a validate function and call the validate function on both init and shift functions.
Check the following
class Point:
MaxScreenSize=10
def __init__(self,x,y):
self.xcoord=x
self.ycoord=y
self.validate()
def validate(self):
if 0>self.xcoord or self.xcoord>Point.MaxScreenSize or 0>self.ycoord or self.ycoord>Point.MaxScreenSize:
Point.isValidPt=False
else:
Point.isValidPt=True
def translateX(self,shiftX):
self.xcoord=self.xcoord+shiftX
self.validate()
def translateY(self,shiftY):
self.ycoord=self.ycoord+shiftY
self.validate()
in the above code each time the validation will perform and update the values
of isValidPt.
Here is the working code:
def g(y=10):
return y**2
def f(x,y=10):
return x*g(y)
print(f(5)) #->500
However, let's suppose we don't want to remember and copy a default value of keyword parameter y to the definition of external function (especially if there are several layers of external functions). In the above example it means that we want to use parameter, already defined in g.
One way to do that:
def f(x,y=None):
if y==None: return x*g()
else: return x*g(y)
But is there a cleaner way to do the same?
Something like:
def f(x,y=empty()):
return x*g(y)
Interesting question! Here's another possibility, however this requires handing in the second parameter as a named parameter.
>>> def g(y=10):
... return y**2
...
>>> def f(x, **kwargs):
... return x * g(**kwargs)
...
>>> f(5)
500
>>> f(5, y=0)
0
A limitation of signatures such as def f(x, y=None) or def f(x, **kwargs) is that readers have to dig into source code or documentation to find out what's going on with y. Stick to something simple and straightforward:
DEFAULT_Y = 10
def g(y=DEFAULT_Y): ...
def f(x, y=DEFAULT_Y): ...
This is possible:
def g(y=10):
return y**2
def f(x, y=g.__defaults__[0]):
return x * g(y)
But it is arguably less clear than what you had originally (defaulting y to None).
An option which doesn't restrict the definition order of f and g, and should remain working if the function default of g gets changed dynamically:
def f(x, y=None):
kwargs = {}
if y is None:
kwargs['y'] = y
return x * g(**kwargs)
I'd like to start by saying if the arguments were keyword only this would be so easy:
def f(*, x="x", y= "y",z="z"):
print(x,y,z)
def g(*, x,y,z):
print(x,y,z,"from g!!")
if g.__kwdefaults__ is None: #completely override defaults
g.__kwdefaults__ = f.__kwdefaults__
else: #if there are already some defaults then update
g.__kwdefaults__.update(f.__kedefaults__)
g()
if you are using positional arguments it isn't quite as easy although your example is one of the specific cases that works the same way:
def g(y=10): #last argument is y
return y**2
def f(x,y): #last argument is y
return x*g(y)
f.__defaults__ = g.__defaults__ #copies the end of the defaults to f
print(f(5)) #->500
But this is a very specific case:
The arguments to inherit the defaults must be in the same order as the original.
There must not be any positional arguments after the ones with inherited defaults
There must not be any other arguments with default values (or they get overridden)
The generic solution requires quite a bit of code but allows any signature to be merged into another, for example:
def f(x,y,z=0,reverse=True):
pass
#copy_defaults(f)
def g(a,b, #arguments for g
x,y,z, #arguments to forward to f
c=None, d="test", #some optional arguments for g
*,reverse): #only take reverse as a keyword
pass
>>> inspect.signature(g)
<Signature (a, b, x, y, z=0, c=None, d='test', *, reverse=True)>
This can be achieved with the following code (I can't find a simpler way to do it that works with above case)
import inspect
def copy_defaults(original_f):
"creates wrapper for DefaultArgs(original_f).copy_defaults(dest_f)"
def wrapper(dest_f):
return DefaultArgs(original_f).copy_defaults(dest_f)
return wrapper
class DefaultArgs(dict):
def __init__(self,func):
spec = inspect.getfullargspec(func)
if spec.defaults:
dict.__init__(self,
zip(reversed(spec.args),
reversed(spec.defaults)
))
else:
dict.__init__(self) #I'm not sure this is necessary
if spec.kwonlydefaults:
self.update(spec.kwonlydefaults)
def get_kwdefaults(self,keywords):
return {k:v for k,v in self.items() if k in keywords}
def gen_pos_defaults(self,args,defaults=None):
if defaults is None:
defaults = ()
found_default = False
for i,arg in enumerate(args,start=len(defaults)-len(args)):
if arg in self:
yield self[arg]
found_default = True
elif i>=0:
yield defaults[i]
elif found_default: #if an argument does not have a default but is after one that does
raise TypeError("non-default argument %r follows default argument"%arg)
def copy_defaults(self,func):
spec = inspect.getfullargspec(func)
new_kwargs = self.get_kwdefaults(spec.kwonlyargs)
if func.__kwdefaults__ is not None:
func.__kwdefaults__.update(new_kwargs)
else:
func.__kwdefaults__ = new_kwargs
func.__defaults__ = tuple(self.gen_pos_defaults(spec.args,spec.defaults))
return func
If you can modify g, then this works:
def g(y=None):
if y is None:
y = 10
return y**2
def f(x,y=None):
return x*g(y)
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.
When I try to enter the code below, I get [None, None] printed on the console rather than the expected [3, 3] and was wondering what would help to fix this.
class Blah(object):
def track(self,dot):
self.dot = dot
class Second(Blah):
def __init__(self,arg):
self.blocky = []
x = 0
while x < 2:
self.blocky.append(Blah.track(self,arg))
x += 1
bleh = Second(3)
print bleh.blocky
Among other more minor issues, your track method doesn't return anything, so you're passing the returned value of a function that returns nothing (None in other words) into that list.
The following worked for me:
class Blah(object):
def track(self, dot):
self.dot = dot
return self.dot
class Second(Blah):
def __init__(self, arg):
self.blocky = []
x = 0
while x < 2:
self.blocky.append(self.track(arg))
x += 1
Blah.track doesn't have a return statement, so it returns None.
You could fix this by doing:
class Blah(object):
def track(self, dot):
self.dot = dot
return dot
Also, you're calling Blah.track(self, dot) when you could just be calling self.track(dot), since self is a Second, which is a subclass of Blah.
That might look like this:
class Second(Blah):
def __init__(self,arg):
self.blocky = []
x = 0
while x < 2:
self.blocky.append(self.track(arg))
x += 1
The track method isn't returning anything. Perhaps you meant this?
def track(self, dot):
self.dot = dot
return dot
Also, since Second inherits from Blah you can replace
Blah.track(self, arg)
with
self.track(arg)