如何在Python中控制函数的参数精度

时间:2012-04-06 21:43:03

标签: python function parameters

我在Python中有这个功能:

def Rotate_Vector(vector, axis, direction):

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

我希望在我的函数中控制输入参数是否正确:

  • vector 必须是3整数
  • 的元组
  • 必须是3个整数的元组,并且可能的值应为-1,0,1
  • direction 必须是值为+1或-1的整数。

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

编辑:

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

3 个答案:

答案 0 :(得分:3)

简短的回答是:不要。 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")

等...

然而,这违反了Python 请求宽恕而非许可的原则。

我还要注意,这里更好的选择是避免使用魔术数字。例如,添加Vector.FORWARD = +1Vector.BACKWARD = -1,然后告诉人们将这些传递到方向。这仍然具有灵活性,但可以为人们指导使用方向。同样,您可以为矢量提供namedtuples,以便在构建它们时提供指导。

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

答案 1 :(得分:2)

我会避免检查用户发送的容器类型。正如其他回答者所说,这会对代码造成无用的限制,从而阻止代码重新使用。从接口的角度来考虑它。只要参数符合接口,代码就应该对它们起作用并产生所需的结果。

此逻辑不适用于您希望强加的两个特定约束。具体来说,您希望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当然不是方向的有效输入。检测您知道的条件会导致算法失败并引发有用的错误消息,这对于您的代码用户来说是体贴的,因为它们允许他们发送任何有意义的内容。

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

答案 2 :(得分: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,您可能希望为ValueError筹集vector

如果你实际上不需要ax / ay / az或x / y / z,最好直接在axisaxes上执行操作,并让底层操作提升例外。这将使它能够进行鸭子打字。

修改:(更新axis - > {{1}}了解问题中的新值)