Sympy(gometry子包)中发现的一个bug

  • 问题来源:获取点到线段的距离。
  • 点到线段的距离与点到直线的距离不同,点到线段的最短距离并不一定是垂直距离。但是当使用sympy.geometry.line中的Segmnet类创建实例化的线段,并使用Segment类中的 distance() 方法获取 某个点到这条线段的距离时,发现无论点在何处,计算的始终是点到直线的距离(即垂直距离)。
p1_sym = sympy.geometry.point.Point(1, 0)
p2_sym = sympy.geometry.point.Point(0, 1)
p3_sym = sympy.geometry.point.Point(10, 0)
s1_sym = sympy.geometry.line.Segment(p1_sym, p2_sym)
print(s1_sym.distance(p3_sym))

结果输出为 9*sqrt(2)/2,这显然是不对的。
  • Debug后发现,调用distance() 方法,始终会跳至 Line 类中的distance() 方法,而不是 Segment 类中的 distance() 方法。究其原因,是因为Segment3D这个类继承了Segment, LinearEntity2D两个类,而 LinearEntity2D,有继承了Line类,最后就使用了Line 类中的distance() 方法。
sympy中Segment2D的继承
class Segment2D(LinearEntity2D, Segment):

将继承先后顺序修改,结果正确。继承顺序改为:

class Segment2D(Segment, LinearEntity2D):

看到这里,bug的原因找到了,是Python的多重继承问题引起的!!!

Python多重继承

新式类和经典类

  • Python3 中全部默认是新式类,新式类和经典类的多重继承关系不同。在菱形继承(或称钻石继承)中,经典类多继承属性搜索顺序: 先深入继承树左侧,若没有搜索到,开始找右侧;新式类多继承属性搜索顺序: 先水平搜索,然后再向上移动。
  • 更多经典类和新式类的区别,请自行检索,但个人认为没必要纠结于二者的区别,新式类是未来,掌握新式类就完全OK。

继承关系

这里多重继承牵涉到的都是新式类。

1. 简单多重继承
class Line():
    def distance(self):
        print('value of A')

class LinearEntity(Line):
    pass

class Segment():
    def distance(self):
        print('value of C')

class Segment2D(LinearEntity, Segment):
    pass

test = Segment2D()
test.distance()
打印结果 : value of A

更改Segment2D类对父类的继承顺序:

class Line():
    def distance(self):
        print('value of A')

class LinearEntity(Line):
    pass

class Segment():
    def distance(self):
        print('value of C')

class Segment2D(Segment, LinearEntity):
    pass

test = Segment2D()
test.distance()
打印结果 : value of c

结论:简单多重继承,先一直深入搜索左边,没有找到相关属性/方法,再搜索右边

2. 菱形继承

当一个子类继承2个父类,而2个父类又都继承一个基类,构成了一个菱形.

python 求过一点垂直于直线的垂足 python点到直线距离_Line

为验证菱形继承的继承关系,修改上面的代码,如下:

class Line():
    def distance(self):
        print('value of A')

class LinearEntity(Line):
    pass

class Segment(Line):
    def distance(self):
        print('value of C')

class Segment2D(LinearEntity, Segment):
    pass

test = Segment2D()
test.distance()
打印结果 : value of c

更改Segment2D类对父类的继承顺序:

class Line():
    def distance(self):
        print('value of A')

class LinearEntity(Line):
    pass

class Segment():
    def distance(self):
        print('value of C')

class Segment2D(Segment, LinearEntity):
    pass

test = Segment2D()
test.distance()
打印结果 : value of c

可以看到,不管继承先后,菱形继承关系中,都是先左右搜索,再上下搜索,即一层一层搜索。