2

我在 Python 中有这个函数:

def Rotate_Vector(vector, axis, direction):

其中vector是3个元素的元组(每个元素代表笛卡尔坐标轴上向量的三个坐标x,y,z),axis是轴的坐标,direction是一个整数,表示顺时针或逆时针运动。

我想在我的功能中控制输入参数是否正确:

  • 向量必须是 3 个整数的元组
  • axis必须是 3 个整数的元组,并且可能的值应该是 -1, 0, 1
  • 方向必须是值为 +1 或 -1 的整数。

我想知道在函数中执行这些控件(类型、值和元素数量)的正确方法。

编辑:

可能是 6 种可能的情况: (1,0,0) (-1,0,0) (0,1,0) (0,-1,0) (0,0,1) (0,0,- 1)

4

3 回答 3

4

简短的回答是:不要。Python 是鸭子类型的,所以只要做你需要做的,如果它不起作用,那么它就会出错。

例如,如果将向量限制为长度为 3 的元组,那么传入列表的人呢?如果它做同样的工作,为什么它们传递的内容对你很重要?

如果您真的觉得需要,请执行以下操作:

if direction not in {1, -1}:
    raise ValueError("direction must be +1 or -1")

if not len(vector) == 3:
    raise ValueError("vector must contain 3 values")

ETC...

但是,这违反了 Python请求原谅而不是许可的原则。

我还要注意这里更好的选择是避免使用幻数。例如,添加,Vector.FORWARD = +1然后Vector.BACKWARD = -1告诉人们将它们传递给方向。这仍然提供了灵活性,但为人们提供了关于使用什么来指导的指导。同样,您可以提供namedtuples向量以在构建它们时提供指导。

还值得注意的是,PEP-8建议lowercase_with_underscores使用函数名称,因此Rotate_Vector()这不是一个特别好的函数名称,除非您被项目中现有的约定所强制。

于 2012-04-06T21:44:13.833 回答
3

我会避免检查用户发送的容器类型。正如其他回答者所指出的那样,这对您的代码施加了无用的限制,从而阻止了代码的可重用性。从接口的角度来考虑。只要参数符合接口,那么代码就应该对它们起作用并产生所需的结果。

但是,此逻辑不适用于您想要施加的两个特定约束。具体来说,您希望axis变量的条目位于{1, 0, -1}并且您希望方向变量位于{1, -1}.

这些是有效的约束。我会这样实现它们:

valid_axis_entries = set((1,0, -1))
valid_direction_values = set((1, -1))

def rotate_vector(vec, axis, direction):
    if not all(entry in valid_axis_entries for entry in axis):
        raise SomeErrorCondition

    if direction not in valid_direction_values:
        raise SomeOtherErrorCondition

我遵循的一般规则是允许任何有意义的输入飞行,并允许任何raise没有意义的输入出现错误。任何序列都对向量有意义,但根据您的算法,没有任何序列值对您的旋转轴有意义,并且itertools.islice肯定不是方向的有效输入。检测你知道的条件会导致你的算法失败并提出有用的错误消息对于你的代码用户来说就像允许他们发送任何有意义的东西一样体贴。

另外,我支持将方向参数存储为大写命名变量的建议,并鼓励用户发送这些而不是依赖于魔术常量。

于 2012-04-06T23:29:01.547 回答
1

如前所述,python 是鸭子类型的,因此您只需像往常一样做事,并期望用户处理引发的任何异常。

最好只对您的代码进行单元测试,确保所有内容都使用有效数字,这会增加您对某些工作的信心,并允许您更轻松地缩小可能的错误位置。

如果你真的想检查类型,你可以对对象的类型(例如isinstance(direction, int))使用断言来进行调试,但这实际上只是“穷人的单元测试”。

使用 python 的原则(请求宽恕,而不是许可,并且显式优于隐式),我会做这样的事情:

import math

def rotate_vector(vector, axis, direction):
    try:
        x, y, z = vector
    except TypeError:
        raise TypeError("Invalid vector {0}".format(vector))

    valid_axes = {(1,0,0), (-1,0,0), (0,1,0), (0,-1,0), (0,0,1), (0,0,-1)}

    if not axis in valid_axes:
        raise ValueError("Invalid axis {0}".format(axis))

    try:
        ax, ay, az = axis
    except TypeError:
        raise TypeError("Invalid axis {0}".format(axis))

    # do math to rotate the vector
    # rotated = ...

    try:
        # You really only need the sign of the direction
        return math.copysign(rotated, direction)
        # or:
        return rotated * math.copysign(1, direction)
    except TypeError:
        raise TypeError("Invalid direction {0}".format(direction))

因为你真的只关心方向的标志,你可以使用它并消除任何错误检查。的特殊情况0将被视为1,您可能需要提出一个ValueErrorfor 。

如果您实际上不需要 ax/ay/az 或 x/y/z,最好直接对vectorand执行操作axis,并让底层操作引发异常。这将使它能够用于鸭子打字。

编辑:(更新axes->axis问题中的新值)

于 2012-04-06T22:58:03.880 回答