范围非默认参数跟随默认参数

8

为什么range函数允许一个非默认参数(stop)跟随一个默认参数(start)?

例如:

>>> r = range(1, 2, 3)
>>> print(r.start, r.stop, r.step)
1 2 3
>>> r = range(10)
>>> print(r.start, r.stop, r.step)
0 10 1

尝试模拟签名是明显的违规行为:

def my_range(start=0, stop, end=1):
    pass

我知道它是用C实现的,这可能会导致在Python环境中违反规定的行为。我猜这样做是为了使API更加用户友好,但我没有找到任何支持这一点的来源(源代码不多,PEP 457仅说明了range的奇怪之处)。有人知道为什么要这样做吗?

查看文档:似乎 range 有两个不同的签名!! class range(object) | range(stop) -> range 对象 | range(start, stop[, step]) -> range 对象 - Jean-François Fabre
@Jean-FrançoisFabre 我想我会这样做,因为我并不真正想要自己实现它 :-). - Dimitris Fasarakis Hilliard
1
太晚了,我已经为你做了那件事 :) 现在我无法再关闭问题,但我想我不会这样做。我最好想出一个答案。 - Jean-François Fabre
3
找到了源代码,但并没有解释(没有注释来说明为什么)。我猜测和你一样:只允许指定开始或步长是没有用的,而在循环时必须要同时指定开始和结束比使用C语言的循环更糟糕。也许Raymond Hettinger或Alex Martelli可以回答这个问题。 - Jean-François Fabre
我猜对于他们来说这很明显(但对于我们这些凡人来说不是)。 - Jean-François Fabre
显示剩余2条评论
1个回答

8
我认为这个问题基于一个错误的前提:
它是用C实现的,但行为不会违反“Pythonland”的规定。文档中的函数签名只是不准确(实际上并不是不准确,而是一个“真实签名”的近似值,易于理解)。
例如,`range`甚至不支持命名参数,但根据文档应该支持:
>>> range(stop=10)
TypeError: range() does not take keyword arguments

因此,实现更多地是沿着以下方式进行的:
class range(object):
    def __init__(self, *args):
        start, step = 0, 1
        if len(args) == 1:
            stop = args[0]
        elif len(args) == 2:
            start, stop = args
        elif len(args) == 3:
            start, stop, step = args

这是有效的 Python 代码,与 range 内部实现(在 CPython, Python 3.6.1 中的实际实现可能略有不同,因此不要太严格看待该类)相似。

然而像 range(*args) 这样的签名对于用户来说可能并不是真正有用的(特别是对于那些甚至不知道什么是*args的新手用户)。文档中说明 range 有两种签名: range(stop)range(start, stop[, step]) 可能(从技术上)并不准确,但它“解释”了签名如何被解释。


至于为什么:我没有可靠的来源,但我快速扫描了一下我的代码:

我更经常使用 range(stop),而不是 range(start, stop)range(start, stop, step)。因此,单个参数的情况可能是非常“特殊和常见”的,所以对于它有方便之处。在许多场景下都写 range(0, stop) 非常麻烦。


1
我本来要回答(几乎)相同的事情(只是比那个更简短,且没有那么好的文档)。 - Jean-François Fabre
2
实用性胜过纯粹性。感谢您的撰写;我会在接下来的几天里仔细研究并回复您,如果我发现任何奇怪的地方。 - Dimitris Fasarakis Hilliard
@JimFasarakisHilliard 你发现什么奇怪的东西了吗?xD - MSeifert
抱歉,@MSeifert,我完全忘记了这件事 :-) - Dimitris Fasarakis Hilliard
@JimFasarakisHilliard random.randrange() 遵循相同的思路,但是它是用Python实现的,并带有“这段代码有点混乱,为了常见情况而快速实现”的注释-我认为这强化了它是一个实用的选择 https://github.com/python/cpython/blob/3.6/Lib/random.py#L173 - Chris_Rands

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接