在Python中将对象从类转换为子类的方法

3 投票
3 回答
1008 浏览
提问于 2025-05-10 15:23

考虑以下一个简单的问题:

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()

简单来说,NormalizedVector对象和Vector对象是一样的,只不过多了一个“归一化”的功能。

有没有可能给Vector添加一个方法,这样每当调用normalize方法时,这个对象就会自动变成NormalizedVector类型呢?

我知道我可以使用抽象工厂模式,但这个方法只适用于在创建对象时就进行子类化:我希望能够对子类化那些已经创建的对象。

我找到了一些解决方案,这些方案是通过重新赋值__ class __方法来实现的,但这种做法并不推荐。我希望能把上面的模式改成一个更“Pythonic”的方式。

相关文章:

  • 暂无相关问题
暂无标签

3 个回答

0

如果你真的想保留这两个类,你可以重写这些类的操作。

比如说,你想把一个向量和一个标准化的向量相加。你可以这样做:

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

通过这种方式,你可以重写任何你需要的函数。你可以在文档中找到可能的操作列表。

1

为什么你需要在向量本身里记录归一化的信息,而不是在你的 main.py 或者你使用向量的地方来记录呢?

另外,我建议你返回一个新的对象副本,里面包含归一化后的值。这样一来,你可以在计算时随时创建归一化的向量,而不会改变原来的向量。

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)

如果你有很多需要归一化的向量,可以把它们归一化成两个列表,或者一个元组列表,或者你想要的其他形式,这里有一个字典的例子:

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

如果你只有几个向量,或者偶尔需要归一化一个向量来进行计算,你可以手动记录它们,或者在计算时随时创建,而不改变原来的向量: v_norm = v.normalized()

2

我建议你只使用 Vector() 这个类,并且加一个布尔类型的 normalized 实例属性。

另外,在 normalize 方法中,你使用了 xyz 这几个变量,但它们并没有被定义,而且你也没有用 self 来读取它们。

我推荐的代码是:

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()

输出结果:

False
True
True


__repr__ 函数会很好地展示你的对象的表示方式:

print v1

输出结果:

Vector (0.267261241912, 0.534522483825, 0.801783725737)

撰写回答