user151768
user151768

Reputation: 11

Python: subclass inherits super class attributes, but loses it's other attributes

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

Upvotes: 0

Views: 282

Answers (1)

Daniel
Daniel

Reputation: 42778

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

Upvotes: 1

Related Questions