【问题标题】:How can the built-in range function take a single argument or three?内置范围函数如何接受一个或三个参数?
【发布时间】:2012-11-02 05:01:42
【问题描述】:

范围函数如何接受:单个参数range(stop),或range(start, stop),或range(start, stop, step)。它是否使用 variadic 之类的 *arg 参数来收集参数,然后使用一系列 if 语句根据提供的参数数量分配正确的值?本质上,range() 是否指定如果有一个参数,则设置为停止参数,或者如果有两个则它们是startstop,或者如果有三个则将它们设置为分别是stopstartstep?我想知道如果要在纯 CPython 中编写 range 会如何做到这一点。

【问题讨论】:

  • RTSL(阅读源代码,卢克)。
  • 我希望这取决于实现。你正在使用什么实现? cPython?杰通?皮皮?
  • 查看this对问题的回答如何在Python中实现可选的第一个参数(重现 slice() 行为)

标签: python function arguments range


【解决方案1】:

类 xrangeIterator:

def __init__(self,xrange_obj):

    self._xrange_obj=xrange_obj


def __next__(self):

    self.copy_gen=self._xrange_obj._result

    for i in self.copy_gen:

        return i

    raise StopIteration

类 xrange:

def __init__(self,*args):

    self._args=args

    self.start,self.step=0,1

    self._repr_string=None

    for i in self._args:

        if type(i) is not int:

            raise TypeError("Cannot interprate '{}' as integer!".format(type(i).__name__))


    if self._length(self._args)<1:

        raise TypeError( "xrange Must have at least one argument")


    elif self._length(self._args)==1:

        self.stop=self._args[0]

        self._repr_string="xrange(0,{})".format(self.stop)

        self._result=self._xrange(self.start-self.step,self.stop-self.step,self.step)

    elif self._length(self._args)==2:

        self.start=self._args[0]

        self.stop=self._args[1]

        self._result=self._xrange(self.start-self.step,self.stop-self.step,self.step)

    elif self._length(self._args)==3:

        self.step=self._args[2]

        self.start=self._args[0]

        self.stop=self._args[1]

        self._result=self._xrange(self.start-self.step,self.stop-self.step,self.step)


    else:

        raise TypeError("xrange expected at most three arguments,got {}".format(self._length(self._args)))


def __repr__(self):

    if self._length(self._args)==1:

        return self._repr_string

    return "xrange{}".format(self._args)

def __iter__(self):

    return xrangeIterator(self)


def _length(self,n):

    counter=0

    for i in n:

        counter+=1

    return counter


def _xrange(self,start,stop,step):

    if step==0:

            raise ValueError("Argument 3 should not be zero!")

    if start<stop and step<0:

            raise TypeError("argument 3 should not be {}".format(step))

    if start<stop:

        while start<stop:

            start+=step

            yield start

    else:

        while start>stop and step<0:

            start+=step

            yield start

【讨论】:

    【解决方案2】:

    lqc's answer 演示了range 是如何在 C 中实现的。您可能仍然好奇如果 Python 的内置函数是用 Python 编写的,range 将如何实现。我们可以阅读 PyPy 的源代码来找出答案。来自pypy/module/__builtin__/functional.py

    def range_int(space, w_x, w_y=NoneNotWrapped, w_step=1):
        """Return a list of integers in arithmetic position from start (defaults
    to zero) to stop - 1 by step (defaults to 1).  Use a negative step to
    get a list in decending order."""
    
        if w_y is None:
            w_start = space.wrap(0)
            w_stop = w_x
        else:
            w_start = w_x
            w_stop = w_y
    

    第一个参数space,在我看到的所有内置函数中都作为一个参数出现,所以我猜它有点像self,因为用户不直接提供它。在其余三个参数中,其中两个具有默认值;所以你可以用一个、两个或三个参数调用range。每个参数的解释方式取决于提供了多少参数。

    【讨论】:

    • 太好了,不过不用空格参数也可以工作。
    【解决方案3】:

    范围采用 1、2 或 3 个参数。这可以通过def range(*args) 和显式代码来实现,当它获得 0 个或超过 3 个参数时引发异常。

    它不能用默认参数来实现,因为你不能在默认值之后有一个非默认值,例如def range(start=0, stop, step=1)。这本质上是因为 python 必须弄清楚每个调用的含义,所以如果你用两个参数调用,python 需要一些规则来确定你覆盖了哪个默认参数。没有这样的规则,根本不允许。

    如果您确实想使用默认参数,您可以执行以下操作:def range(start=0, stop=object(), step=1) 并明确检查 stop 的类型。

    【讨论】:

    • 您的后一个定义给出了语法错误(此处为 3.3,但 AFAIK 在 2.x 中也无效)。
    • 默认参数只能在末尾,所以如果你想在没有可变参数的纯 Python 中实现它,你必须切换参数。
    • 我曾经写过我自己的range-like 函数,并通过检查传递了多少*args 来确定各种参数的含义(并为其他参数分配适当的默认值) .有点痛苦……但绝对可行。
    • @Marcin 感谢您的回复!它证实了我的想法。我相信如果超过 3 个,而不是 4 个,它会抛出错误。
    • @MWGriffin 相当。已更正。
    【解决方案4】:

    开源软件的美妙之处在于你可以just look it up in the source:

    (TL;DR:是的,它使用可变参数)

    if (PyTuple_Size(args) <= 1) {
        if (!PyArg_UnpackTuple(args, "range", 1, 1, &stop))
            return NULL;
        stop = PyNumber_Index(stop);
        if (!stop)
            return NULL;
        start = PyLong_FromLong(0);
        if (!start) {
            Py_DECREF(stop);
            return NULL;
        }
        step = PyLong_FromLong(1);
        if (!step) {
            Py_DECREF(stop);
            Py_DECREF(start);
            return NULL;
        }
    }
    else {
        if (!PyArg_UnpackTuple(args, "range", 2, 3,
                               &start, &stop, &step))
            return NULL;
    
        /* Convert borrowed refs to owned refs */
        start = PyNumber_Index(start);
        if (!start)
            return NULL;
        stop = PyNumber_Index(stop);
        if (!stop) {
            Py_DECREF(start);
            return NULL;
        }
        step = validate_step(step);    /* Caution, this can clear exceptions */
        if (!step) {
            Py_DECREF(start);
            Py_DECREF(stop);
            return NULL;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2020-11-16
      相关资源
      最近更新 更多