函数定义如range

5

假设我想编写一个类似于range的函数。

回想一下,range有一个参数和两个/三个参数的形式:

class range(object)
 |  range(stop) -> range object
 |  range(start, stop[, step]) -> range object

如果我想让方法或函数具有相同的接口,是否有比这更优雅的方式:

def range_like(*args):
    start,stop,step=[None]*3
    if len(args)==1:
        stop=args[0]
    elif len(args)==2:
        start,stop=args
    elif len(args)==3:
        start,stop,step=args
    else:
        raise ValueError       
    print(start,stop,step)
3个回答

5

这里没有太多建议,但一个替代方案是使用可选参数,并在只提供一个参数时交换前两个参数:

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

3
我没有给你的回答点踩,但是如果None是一个有效的参数,那么你两个回答都有问题。考虑到slice()接受类似的参数,slice(1)需要等同于slice(None, 1, None),但是slice(1, None)需要等同于slice(1, None, None) - Andrew Clark
1
在这种情况下,您可以使用哨兵对象。但是,在Python中使用None作为缺失参数标记是标准做法。 - andrew cooke

4
我会将其翻译为:“我会写成这样:”
def range_like(start=None, stop=None, step=1):
    if stop is None:
        start, stop = stop, start
    ...

如果这正是你想要的,那么呢?

[更新] 你也可以添加:

    if stop is start is None:
        raise ValueError()

此外,使用哨兵对象而不是 None(这在 Python 中并不常见,但有时会看到):
NOTSET = object()
def range_like(start=NOTSET, stop=NOTSET, step=1):
    if stop is NOTSET:
        start, stop = stop, start

这允许使用 None 作为参数。


1
为什么这个回答会被踩了两次?其他人后来也发表了几乎相同的回答。我们都做错了什么吗?如果你踩了这个回答,请解释一下原因。 - andrew cooke
2
应该更明确地指出第一个参数不是可选的。如果您使用def range_like(start, stop=None, step=1),调用range_like()将会产生更有用的错误信息。 - FastTurtle

2

在必选参数之前无法使用可选参数。

您可以想出解决方法,例如交换参数:

def range_like(s, t=None, u=None):
    if t is None: 
        s,t = t,s

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