range 函数如何采用:单个参数、range(stop)
、 或range(start, stop)
、 或range(start, stop, step)
。它是否使用variadic
诸如*arg
收集参数之类的参数,然后根据提供的参数数量使用一系列 if 语句来分配正确的值?本质上,确实range()
指定如果有一个参数,则将其设置为停止参数,或者如果有两个,则它们是start
, 和stop
,或者如果有三个则将它们分别设置为stop
,start
和step
?我想知道如果要在纯 CPython 中编写 range 将如何做到这一点。
4 回答
Range 采用 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
.
开源软件的美妙之处在于您可以在源代码中查找它:
(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;
}
}
lqc 的回答演示了如何在 C中实现。如果 Python 的内置函数是用 Python 编写的,range
您可能仍然对如何实现感到好奇。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
使用一个、两个或三个参数进行调用。如何解释每个参数取决于提供了多少参数。
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