5

由于在 python 3.X 中,build-idrange()函数返回的不再是一个列表,而是一个可迭代的,一些旧代码在我range()用来方便地生成我需要的列表时失败了。

所以我尝试lrange像这样实现我自己的功能:

def lrange(start = 0, stop, step = 1):
    ret = []
    while start < stop:
        ret.append(start)
        start += step
    return ret

给我一个“非默认参数遵循默认参数”解释器错误。

如果我看一下 Python 的range()似乎是可能的。

我发布这个问题主要是因为我想知道是否/如何可以自己实现具有这样签名的功能

4

5 回答 5

10

快速回答

这个问题在我刚开始学习 Python 的时候就出现了,我觉得值得在这里记录一下这个方法。只有一项检查用于模拟原始行为。

def list_range(start, stop=None, step=1):
    if stop is None:
         start, stop = 0, start
    return list(range(start, stop, step))

我认为这个解决方案比使用所有关键字参数或*args.

解释

使用哨兵

正确执行此操作的关键是使用哨兵对象来确定您是否获得第二个参数,如果没有,则在将第一个参数移动到第二个参数时为第一个参数提供默认值。

None,作为 Python 的 null 值,是一个很好的最佳实践哨兵,检查它的惯用方法是使用关键字is,因为它是一个单例。

带有正确文档字符串的示例,声明签名/API

def list_range(start, stop=None, step=1):
    '''
    list_range(stop) 
    list_range(start, stop, step)

    return list of integers from start (default 0) to stop,
    incrementing by step (default 1).

    '''
    if stop is None:
         start, stop = 0, start
    return list(range(start, stop, step))

示范

>>> list_range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list_range(5, 10)
[5, 6, 7, 8, 9]
>>> list_range(2, 10, 2)
[2, 4, 6, 8]

如果没有给出参数,它会引发错误,这与这里的全关键字解决方案不同。

警告

顺便说一句,我希望这只是读者从理论角度考虑的,我不认为这个功能值得维护,除非在中央规范位置使用以使 Python 2 和 3 之间的代码交叉兼容。 Python,使用内置函数将范围具体化为列表非常简单:

Python 3.3.1 (default, Sep 25 2013, 19:29:01) 
[GCC 4.7.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
于 2014-01-06T02:15:21.900 回答
5

我从来没有真正考虑过这么多,但首先,要解决你的问题,你应该能够在你的代码中用 list(range(...)) 包装 range(...) 。

使用关键字参数,您可以实现这样的签名,因为您在调用时不需要指定实际密钥

def f(x=None, y=None, z=None):
    print x, y, z

f(1, 2, 3)
#output: 1 2 3

然后,您可以检查这些值以确定您应该如何处理它们。所以要模拟范围

def f(x=None, y=None, z=None):
    if z is not None: # then all values were assigned
        return range(x, y, z)
    elif y is not None: # then a start stop was set
        return range(x, y):
    else: # only one value was given
        return range(x)

这里的重点不是要成为范围的包装器(如上所述,仅使用列表),而是要提供一些见解,以了解是否真的试图模拟自定义的内置范围签名。

另请记住,这不是一个完整的解决方案,f(z=1)可能会导致上述问题,因此您希望[kwarg]在检查所需时为每个提供合理的默认值kwarg

def f(x=0, y=None, z=1):
    if y is None:
        raise Exception()
    return range(x, y, z)

对于带有 ([start], stop, [step]) 等签名的 python 方法会更有洞察力

于 2011-12-26T15:50:58.707 回答
2

关于什么:

>>> def my_range(*args):
...     return list(range(*args))
... 
>>> my_range(0, 10, 2)
[0, 2, 4, 6, 8]
于 2011-12-26T15:50:17.270 回答
1

你可以尝试这样的事情:

def lrange(*args):
    # Default values
    start = 0
    step = 1

    # Assign variables based on args length
    if len(args) == 1:
        stop, = args
    elif len(args) == 2:
        start, stop = args
    elif len(args) == 3:
        start, stop, step = args
    else:
        raise TypeError('lrange expected at most 3 arguments, got {0}'
                        .format(len(args)))
     ...

如果您尝试range在解释器中使用,您会发现它不接受关键字参数,因此它肯定会像上面的示例一样围绕可变数量的参数玩一些技巧。

于 2011-12-26T15:52:46.520 回答
1

您可以使用*args**kwargs功能:

def myrange(*args, **kwargs):
  start = 0
  stop = None
  if (len(args) == 1):
    stop = args[0]
  if (len(args) >= 2 ):
    start = args[0]
    stop = args[1]
  start = kwargs.get("start", start)
  stop = kwargs.get("stop", stop)
  return list(range(start, stop))

您需要进行更多的错误检查,并支持 step 运算符。

在这种情况下,您最好像这样实现它:

def myrange(*args):
  return list(range(*args))

请注意,内置range功能不支持kwargs.

于 2011-12-26T16:10:38.703 回答