range 的奇怪参数

17

在Python3中,range函数需要三个参数,其中两个是可选的。因此,参数列表看起来像:

[start],stop,[step]

这意味着(如果我理解错误,请纠正),一个非必需参数出现在一个必需参数之前。但是,如果我尝试定义这样的函数,我会得到如下结果:

>>> def foo(a = 1, b, c = 2):
    print(a, b, c)
SyntaxError: non-default argument follows default argument

作为一个“普通”的Python用户,我是否无法做到这一点,还是可以定义这样的一个函数?当然,我可以这样做:

def foo(a, b = None, c = 2):
    if not b:
        b = a
        a = 1

例如,帮助函数将显示奇怪的信息。因此,我真的想知道是否可以定义一个像内置的range函数那样的函数。


1
请看 https://dev59.com/bm855IYBdhLWcg3wy3sc#4137838 - mattjbray
正如你现在所知道的,range是一个C函数,由于某种原因它没有遵循Python相同的规则(希望能知道为什么)。 人们可能会因为我建议这样做而讨厌我,但自从我对事物的顺序有可怕的记忆以来,我一直在使用range。在我看来,这不应该成为问题,所以我正在解决它:range(*{'start':0,'stop':10,'step':2}.values()) - Charlie Parker
4个回答

21

range()需要一个必选参数和两个可选参数,并且根据传入的参数数量不同而解释这些参数的含义。

如果只传入了一个参数,那么它会被视为stop参数,否则第一个参数将被解释为起始位置。

实际上,range()是用C语言编写的,它需要可变数量的参数。你可以像这样模拟它:

def foo(*params):
    if 3 < len(params) < 1:
        raise ValueError('foo takes 1 - 3 arguments')
    elif len(params) == 1
        b = params[0]
    elif:
        a, b = params[:2]
    c = params[2] if len(params) > 2 else 1

但你也可以交换参数:

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

你的最终示例无法运行。TypeError:range()不接受关键字参数。 - Rob Smallshire
@RobSmallshire:最后一个示例是range()Python替代方案。我使用关键字参数来模拟range()方法所采用的可选参数;另一种选择是使用*args 捕获所有参数并从中解析出两个值,但那会变得更冗长。这是我答案中的第一个示例。 - Martijn Pieters
@RobSmallshire:OP试图使用关键字参数模拟range()的行为,第二部分是为了说明如何通过使用关键字参数(在Python代码中)实现行为(接受1到3个位置参数)。 - Martijn Pieters
1
所以,这只是因为C编码版本使用*params而不是实际定义参数...但他们为什么要这样编码呢?如果函数返回一个错误,而你的参数长度大于3,我不明白为什么要使用*params而不是参数,尤其是。看起来就像非常粗糙的编码...是因为它更快吗?对我来说,它应该被编码为range(PyObject *stop, PyObject *start, PyObject *step),而不是处理这个问题。对我来说,这似乎是一个非常奇怪的决定... - Corey Levinson
@CoreyLevinson:不行,因为那样你就必须总是指定起始和步长值。Python核心开发人员明确选择支持range(stop)range(start, stop)两种方式。 - Martijn Pieters
显示剩余3条评论

17

range不接受关键字参数:

range(start=0,stop=10)
TypeError: range() takes no keyword arguments

它接受1、2或3个位置参数,根据它们的数量进行评估:

range(stop)              # 1 argument
range(start, stop)       # 2 arguments
range(start, stop, step) # 3 arguments

即不能使用默认的start创建一个带有定义的stopstep的范围。


有没有办法让 range 函数在不编写虚拟函数或其他东西的情况下接受参数...? - Charlie Parker
1
找到了自己问题的答案:range(*{'start':0,'stop':10,'step':2}.values()) - Charlie Parker

1
def foo(first, second=None, third=1):
     if second is None:
         start, stop, step = 0, first, 1
     else:
         start, stop, step = first, second, third

0

正如你现在所知道的,真正的答案是range是一个C函数,由于某种原因它没有遵循Python相同的规则(知道为什么会很好)。

人们可能会因为我建议这样做而讨厌我,但自从我对事物的顺序有可怕的记忆以来,我一直在使用range。在我看来,这不应该成为问题,所以我正在解决它:

range(*{'start':0,'stop':10,'step':2}.values())

我之所以把它变成一行代码,是因为我不想定义一个需要在每个地方都定义或导入的范围函数。这是纯Python。


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