关于python:语义版本控制对参数名称的更改意味着什么?

What does semantic versioning imply about parameter name changes?

在试图向朋友解释语义版本控制的重要性时,我面临以下难题。

假设我们有版本为1.2.3的库libfoo,它具有以下功能:

1
2
3
4
5
6
7
8
def foo(x, y):
   """
    Compute the sum of the operands.
    :param x: The first argument.
    :param y: The second argument.
    :returns: The sum of `x` and `y`.
   """

    return x + y

现在假定此函数及其文档更改为:

1
2
3
4
5
6
7
8
def foo(a, b):
   """
    Compute the sum of the operands.
    :param a: The first argument.
    :param b: The second argument.
    :returns: The sum of `a` and `b`.
   """

    return a + b

我的第一印象是说下一个版本将是1.2.4,因为公共接口未更改。例如,调用这样的函数的人根本不会注意到更改:

1
foo(3, 4)

但是再想想,考虑到Python允许通过名称来指定参数,这很可能是API中断。如果有人像这样调用我的函数:

1
foo(y=4, x=3)

对于版本1.2.4,这将不再起作用,这会破坏语义版本控制协定。

另一方面,这样的更改似乎很小,以至于我无法将版本增加到2.0.0

总而言之,这是否构成API中断?在这种情况下,下一个版本号应该是什么?


简短的答案:是的,我认为这将构成API中断,因此可能会增加主版本号。请注意以下注意事项。

当公开公共/外部API时,您要承担额外的"责任",以仔细考虑接口的更改。例如,这包括推迟进行潜在的改进以避免破坏向后兼容性*。在维护API时,应非常谨慎地考虑使用接口会合法影响任何代码**的任何更改。

语义版本控制的规范是明确的:

Major version X (X.y.z | X > 0) MUST be incremented if any backwards incompatible changes are introduced to the public API.

正如您在问题中所确定的那样,更改参数名称会导致向后不兼容,导致代码无法通过关键字传递参数。

但是,与其说该更改应该增加主要版本,不如说是我不应该做出更改,或者至少不是孤立地做出更改-这太微小了,不足以证明该主要增加有可能破坏现有的有效代码。需要。除了以下情况之一:

  • 这是一些较大的重要更改的一部分。要么
  • 您的示例中没有显示出更改的确有充分的理由(某些显示停止的错误或其他依赖于此的功能);
  • 我会完全推迟更改。最好走得更慢,并确保继续满足语义版本控制合同,只有在有充分理由的情况下才能进行此类更改。

    *正如我们在Python标记中一样,请考虑整数除法,尽管它被BDFL视为错误,但至今仍保留在2.x版本中。

    **我说"合法"是指不使用未正式记录的代码,例如,通过访问常规私有属性来访问代码-他们有时会感到不便!因此,如果您已预料到了这种变化,并且明确指定仅应使用位置参数,那么这种变化就可以了,但这是一个奇怪的选择。


    这种类型的更改可能会在发行规模上分为许多不同的领域。

    重大更改(从1.x增加到2.x)

    这违反了您的API合同,可以视为重大更改。但是,最大的警告是这是否是唯一的更改。如果是这样,我不会对此进行重大更改。另一方面,如果这是也破坏您的API合同的众多更改之一,那么我认为增加Major版本是合理的。

    微小变化(从1.2增至1.3)

    从Python文档中借用:

    the minor version number [is] incremented for less earth-shattering changes.

    对我来说,这是一个很小的变化。如您所说,如果用户未命名参数,他们甚至不会注意到已发生更改。

    微变化(从1.2.3递增到1.2.4)

    这是您的错误修复级别更改。如果由于错误而将foo(x, y)更改为foo(a, b),则此修补程序证明微点增量是合理的。

    我对此类更改的看法是使其成为次要更改。当然,这不是"破天荒"的更改,但确实有可能迫使最终用户更改代码。我不会将其归类为重大更改,因为它仅在函数调用上更改参数名称,否则将以完全相同的方式运行该函数,返回完全相同的数据,并以完全相同的数据作为参数,但只是将它们应用于不同的名称在功能内。