How can the built-in range function take a single argument or three?

前端 未结 4 1801
借酒劲吻你
借酒劲吻你 2020-12-06 05:44

I would like to know, if anyone can tell me, how the range function can take either: a single argument, range(stop), or range(start, stop), or

相关标签:
4条回答
  • 2020-12-06 05:58

    The beauty of open-source software is that you can just look it up in the source:

    (TL;DR: yes, it uses varargs)

    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;
        }
    }
    
    0 讨论(0)
  • 2020-12-06 05:58

    lqc's answer demonstrates how range is implemented in C. You may still be curious about how range would be implemented if Python's built in functions were written in Python. We can read PyPy's source code to find out. From 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
    

    The first argument, space, appears as an argument in all the built-in functions I saw, so I'm guessing it's kind of like self in that the user does not directly supply it. Of the remaining three arguments, two of them have default values; so you can call range with one, two, or three arguments. How each argument is interpreted depends upon how many arguments were supplied.

    0 讨论(0)
  • 2020-12-06 06:08

    Range takes, 1, 2, or 3 arguments. This could be implemented with def range(*args), and explicit code to raise an exception when it gets 0 or more than 3 arguments.

    It couldn't be implemented with default arguments because you can't have a non-default after a default, e.g. def range(start=0, stop, step=1). This is essentially because python has to figure out what each call means, so if you were to call with two arguments, python would need some rule to figure out which default argument you were overriding. Instead of having such a rule, it's simply not allowed.

    If you did want to use default arguments you could do something like: def range(start=0, stop=object(), step=1) and have an explicit check on the type of stop.

    0 讨论(0)
  • 2020-12-06 06:13

    class 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
    

    class 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
    
    0 讨论(0)
提交回复
热议问题