Daniel Arteaga
Daniel Arteaga

Reputation: 477

Method to convert object from class to subclass in Python

Consider the following minimal problem:

from math import sqrt    

class Vector(object):

    def __init__(self, x, y, z):
        self.v = [x, y, z]

    def normalize(self):
        x, y, z = self.v
        norm = sqrt(x**2 + y**2 + z**2)
        self.v = [x/norm, y/norm, z/norm]

    # other methods follow  

class NormalizedVector(Vector):

    def __init__(self, x, y, z):
        super(Vector, self).__init__(x, y, z)
        self.normalize()

So essentially NormalizedVector objects are the same as Vector objects but with the added normalization.

Would it be possible to add a method to Vector so that whenever the normalize method is called the object is automatically subclassed to NormalizedVector?

I know that I could be using the abstract factory pattern, but this would only work if objects are subclassed at creation: I would like to be able to subclass objects that have already been created previously.

I have found some solutions based on reassigning the __ class __ method, but these are discouraged. I am willing to modify the pattern above to a one that it is more "Pythonic".

Upvotes: 3

Views: 1029

Answers (3)

Mahi
Mahi

Reputation: 21893

Why do you need to keep track of the normalization in the vector itself, just keep track of it in your main.py or wherever you're using the vectors?

Also, I would simply return a new copy of the object with the normalized values. This way you can create normalized vectors on the fly in your calculations without changing the original vector.

from math import sqrt    

class Vector(object):

    def __init__(self, x, y, z):
        self.v = [x, y, z]

    def normalized(self):
        x, y, z = self.v
        norm = sqrt(x**2 + y**2 + z**2)
        return Vector(x/norm, y/norm, z/norm)

If you have really many vectors you need to normalize, you can normalize them into two lists or a list of tuples or whatever you want, here's a dict example:

vectors = {}
for x, y, z in zip(range(10), range(10), range(10)):
    v = Vector(x, y, z)
    vectors[v] = v.normalize()

If you just have a few vectors or randomly need to normalize a vector for a calculation every now and then, you can just keep track of them manually or create them on the fly in the calculations without changing the original vector: v_norm = v.normalized().

Upvotes: 1

Rolf Lussi
Rolf Lussi

Reputation: 615

If you really want to keep the two classes you could overwrite the operations for the classes.

Let's say you want to add the vector and the normalized vector. You could do it like this

from math import sqrt

class Vector:

    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __add__(self,v):
        if isinstance(v,Vector):
            return (self.x+v.x,self.y+v.y)
        elif isinstance(v,NormVector):
            n = NormVector(self.x,self.y)
            return (n.x+v.x,n.y+v.y)

class NormVector:

    def __init__(self,x,y):
        self.x = x / sqrt(x**2 + y**2)
        self.y = y / sqrt(x**2 + y**2)

    def __add__(self,v):
        if isinstance(v,Vector):
            n = NormVector(v);
            return (self.x + n.x,self.y + n.y)
        elif isinstance(v,NormVector):
            return (self.x+v.x,self.y+v.y)


a = Vector(5,0)
b = NormVector(0,3)
c = a + b
print c

Like this you can overwrite any function you need. A list of possible operations can you find in the documentation

Upvotes: 0

I would recommend to use only the Vector() class and with a boolean normalized instance attribute.

Also, in normalize method you are using x, y and z but those variables are not defined and you are not even reading them with self.

The code I would recommend:

from math import sqrt    

class Vector(object):

    def __init__(self, x, y, z, normalized=False):
        self.v = [x, y, z]
        if normalized in [True, False]:
            self.normalized = normalized
        else:
            raise Exception("Please set normalized to either True or False")

    def __repr__(self):
        return "Vector ({}, {}, {})".format(*self.v)

    def normalize(self):
        x,y,z = self.v
        norm = sqrt(x**2 + y**2 + z**2)
        self.v = [x/norm, y/norm, z/norm]
        self.normalized = True

    def isNormalized(self):
        return self.normalized

    # other methods follow  

v1 = Vector(10, 20, 30)
print v1.isNormalized()
v1.normalize()
print v1.isNormalized()

v2 = Vector(40, 50, 60, normalized=True)
print v2.isNormalized()

Output:

False
True
True


The __repr__ function will just show a good representation of your object:

print v1

Output:

Vector (0.267261241912, 0.534522483825, 0.801783725737)

Upvotes: 2

Related Questions