Python 在“xrange”内使用动态计算的“步长”

Python 在“xrange”内使用动态计算的“步长”,python,Python,假设我有: def func(n): for i in range(1,100000,2*n+3): ... 很明显,阶跃=2*n+3部分只计算一次 但这对xrange来说保证是一样的吗 根据,xrange是一个延迟计算的序列对象 因此,问题基本上是——哪一部分的评估是懒惰的 是否只有开始范围和xrange都不关心阶跃值是如何得出的;执行表达式并将表达式的结果传递给调用,可以是range、xrange或任何其他可调用对象 那是因为。。。也是一种表达;它是一个;传入调用的

假设我有:

def func(n):
    for i in range(1,100000,2*n+3):
        ...
很明显,阶跃=2*n+3部分只计算一次

但这对xrange来说保证是一样的吗

根据,xrange是一个延迟计算的序列对象

因此,问题基本上是——哪一部分的评估是懒惰的

是否只有开始范围和xrange都不关心阶跃值是如何得出的;执行表达式并将表达式的结果传递给调用,可以是range、xrange或任何其他可调用对象

那是因为。。。也是一种表达;它是一个;传入调用的参数是在将结果传入调用之前计算的所有表达式。这里叫什么并不重要


TLDR;xrange对象传递的是表达式的结果,而不是表达式本身。只要结果是一个整数对象,对象就会将其存储为一个不可变的值,以建立虚拟序列的基础。

延迟求值并不意味着您可以在求值过程中更改求值;在迭代xrange时,您正在处理一个生成器,该生成器是使用您提供的参数创建的。它不能在迭代过程中更改其步骤,至少不能使用内置的步骤。

事实上,xrange是惰性工作的,这意味着参数是在xrange的构造时计算的。事实上,xrange一开始并不知道表达式是什么,但发出的元素是惰性生成的。所以阶跃参数实际上是在调用xrange之前计算出来的。。。因此,xrange。。不知道步骤是如何计算的,因此无法要求重新评估

虽然范围很广。。比这更复杂,因为它可以处理消极步骤等,一个非常基本的实现是:

def xrange(frm,to,step):
    i = frm
    while i < to:
        yield i
        i += step

在其他答案的基础上添加:测试这一点的一个好方法是提供一个函数作为step参数。如果要多次求值,则需要多次调用该函数

def foo():
    print 'foo was called'
    return 25

for i in xrange(0, 100, foo()):
    print i
上述代码输出

foo was called
0
25
50
75

这表明step参数只计算一次。

当您将适当的参数传递到xrange时,Python将构造一个生成器对象,该对象将动态计算值

构造并返回生成器对象后,将设置该对象。它不再关心最初用于计算其构造函数参数的变量是否更改

这一原则不仅适用于xrange,也适用于其他可调用对象。将参数传递给可调用函数后,Python将使用任何变量的当前值计算表达式,并将结果传递给可调用函数。如果在计算可调用参数时使用的任何变量的值发生了更改,Python不会在意,因为只使用了变量的当前值。Python不会在每次传入函数的表达式中使用的变量发生更改时都重新计算函数的参数:


xrange中的阶跃部分是否保证计算一次,或者在某些情况下是否可能在每次迭代时计算?@goodvibration:它是在创建对象之前计算的。所以就一次,不是每一步。我想我开始明白我的困惑是从哪里来的了。我需要把range看作是一个返回数组的函数,而把xrange看作是一个返回迭代器的函数。这几乎解决了所有问题。谢谢你@古德:没错。它们都是可调用对象函数是最常见的可调用对象,但类也是可调用的,xrange类型也是可调用的。调用表达式产生另一个结果;函数的返回值,或类型的新实例。@goodvibration:如果需要创建一个类似于range的对象,其中步骤是动态的,那么不能只传入这样的表达式;您必须传入一个函数或其他可调用函数,以便在每次需要时生成步长。然后对象每次都会调用它。您将xrange描述为一个函数,这让我非常清楚,谢谢!是的,我在发布问题后不久就想到了。这里的答案非常有用。谢谢
foo was called
0
25
50
75
>>> n = 10
>>> xr = xrange(n)
>>> n += 1
>>> n
11
>>> list(xr)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>