在Python中将对象从类转换为子类的方法
考虑以下一个简单的问题:
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 个回答
如果你真的想保留这两个类,你可以重写这些类的操作。
比如说,你想把一个向量和一个标准化的向量相加。你可以这样做:
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
通过这种方式,你可以重写任何你需要的函数。你可以在文档中找到可能的操作列表。
为什么你需要在向量本身里记录归一化的信息,而不是在你的 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()
。
我建议你只使用 Vector() 这个类,并且加一个布尔类型的 normalized
实例属性。
另外,在 normalize
方法中,你使用了 x、y 和 z 这几个变量,但它们并没有被定义,而且你也没有用 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)