0

So, I've written a class to represent vectors called Vector, and i'm trying to write a subclass called Visual_vector that contains additional attributes pertaining to how the vector will be displayed on screen, such as the origin and endpoint of the line to be drawn. My issue is that when I create an instance of Visual_vector, it loses self.origin and self.end_point:

>>> a = Visual_vector((0, 0), (45, 330)) >>> a.x 45 >>> a.y 330 >>> a.length 333.0540496676178 >>> a.origin >>> a.end_point 

I suspect this might have something to do with my use of getattr and setattr in the super class, but other than that I have no idea what the issue is

Vector class:

class Vector(): """A class to represent a 2D vector""" def __init__(self, tup): self.x = tup[0] self.y = tup[1] # retrieving attributes def __getattr__(self, name): if name == 'vector': # returns the vector as a tuple # (x, y) return (self.x, self.y) elif name == 'length' or name == 'magnitude': # returns the length as a float # sqrt(x^2 + y^2) return float( sqrt(self.x**2 + self.y**2) ) elif name == 'normal': # returns a normalized vector as a tuple # (x/l, y/l) if self.length == 0: return (0, 0) else: x = self.x / self.length y = self.y / self.length return (x, y) #return (self.x / self.length, self.y / self.length) # setting attributes def __setattr__(self, name, value): if name == 'x' or name == 'y': # assign value normally self.__dict__[name] = value elif name == 'length': # |(x, y)| * length # create a new, normalized vector of the same trajectory new_vector = Vector(self.normal) # then multiply it by a scalar to get the desired length self.x = new_vector.x * value self.y = new_vector.y * value elif name == 'vector': # does the same as setting both x and y at the same time if len(value) == 2: # must be a list or tuple self.x = value[0] self.y = value[1] # mathematical operations def __add__(self, other): # (x1 + x2, y1 + y2) x = self.x + other.x y = self.y + other.y return Vector((x, y)) def __iadd__(self, other): return self + other def __sub__(self, other): # (x1 - x2, y1 - y2) x = self.x - other.x y = self.y - other.y return Vector((x, y)) def __isub__(self, other): return self - other 

Visual_vector class:

class Visual_vector(Vector): """A class to represent the vectors shown on screen""" def __init__(self, origin, end_point): # qualities relative to a coord system Vector.__init__(self, end_point) # qualities relative to the screen self.origin = origin self.end_point = end_point 
4
  • 1
    Your __setattr__ silently refuses to set any attribute other than x, y, length, or vector. Commented Mar 29, 2017 at 17:39
  • 1
    And what do you think will be returned from getattr if name is not vector, length or normal? Commented Mar 29, 2017 at 17:40
  • 1
    You should not use such a __setattr__-Method because of your observed wired behaviour. Replace it with a length- or vector-property Commented Mar 29, 2017 at 17:42
  • How would I get a self.length attribute to calculate the length every time it's called? And also, you guys imply that there's a way to change the 'silently refusing to set any other attribute' behavior. How? Commented Mar 29, 2017 at 17:46

1 Answer 1

1

You should not use such a __setattr__-Method because of your observed wired behaviour. Replace it with a length- or vector-property

class Vector(object): """A class to represent a 2D vector""" def __init__(self, tup): self.x = tup[0] self.y = tup[1] # retrieving attributes @property def vector(self): # returns the vector as a tuple # (x, y) return (self.x, self.y) @vector.setter def vector(self, value): self.x = value[0] self.y = value[1] @property def length(self): # returns the length as a float # sqrt(x^2 + y^2) return float( sqrt(self.x**2 + self.y**2) ) magnitude = length @length.setter def length(self, value): # |(x, y)| * length # create a new, normalized vector of the same trajectory x, y = self.normal # then multiply it by a scalar to get the desired length self.x = x * value self.y = y * value @property def normal(self): # returns a normalized vector as a tuple # (x/l, y/l) len = self.length if len == 0: return (0, 0) else: x = self.x / len y = self.y / len return (x, y) # mathematical operations def __add__(self, other): # (x1 + x2, y1 + y2) x = self.x + other.x y = self.y + other.y return Vector((x, y)) def __iadd__(self, other): self.x += other.x self.y += other.y return self def __sub__(self, other): # (x1 - x2, y1 - y2) x = self.x - other.x y = self.y - other.y return Vector((x, y)) def __isub__(self, other): self.x -= other.x self.y -= other.y return self class Visual_vector(Vector): """A class to represent the vectors shown on screen""" def __init__(self, origin, end_point): # qualities relative to a coord system Vector.__init__(self, end_point) # qualities relative to the screen self.origin = origin self.end_point = end_point 
Sign up to request clarification or add additional context in comments.

4 Comments

Note that since you put magnitude = length before you defined the length setter, magnitude doesn't support assignment. This matches the behavior of the questioner's code, but it's not clear whether this was deliberate on either side.
That was not intended on my part, but thank you for pointing it out!
are there some situations in which __setattr__ is more suited than @property (and vice versa), or is it a matter of preference?
__setattr__ is useful if you don't know the names of the attributes at design time.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.