Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么在执行func(*iterable)时CPython调用len(iterable)?_Python_Cpython - Fatal编程技术网

为什么在执行func(*iterable)时CPython调用len(iterable)?

为什么在执行func(*iterable)时CPython调用len(iterable)?,python,cpython,Python,Cpython,最近我正在编写一个下载程序,它使用HTTP范围字段同时下载多个块。我编写了一个Python类来表示范围(HTTP头的范围是一个闭合区间): \uuuu iter\uuuu神奇的方法是支持元组解包: header = {'Range': 'bytes={}-{}'.format(*the_range)} 而len(u范围)是该范围内的字节数 现在我发现'bytes={}-{}'。format(*the_range)偶尔会导致内存错误。经过一些调试,我发现CPython解释器在执行func(*it

最近我正在编写一个下载程序,它使用HTTP范围字段同时下载多个块。我编写了一个Python类来表示范围(HTTP头的范围是一个闭合区间):

\uuuu iter\uuuu
神奇的方法是支持元组解包:

header = {'Range': 'bytes={}-{}'.format(*the_range)}
len(u范围)
是该范围内的字节数

现在我发现
'bytes={}-{}'。format(*the_range)
偶尔会导致
内存错误。经过一些调试,我发现CPython解释器在执行
func(*iterable)
时会尝试调用
len(iterable)
,并且(可能)会根据长度分配内存。在我的机器上,当
len(u范围)
大于1GB时,会出现
MemoryError

这是一个简化的例子:

class C:
    def __iter__(self):
        yield 5

    def __len__(self):
        print('__len__ called')
        return 1024**3

def f(*args):
    return args

>>> c = C()
>>> f(*c)
__len__ called
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError
>>> # BTW, `list(the_range)` have the same problem.
>>> list(c)
__len__ called
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError
C类:
定义(自我):
收益率5
定义(自我):
打印(称为“列印”)
返回1024**3
def f(*args):
返回参数
>>>c=c()
>>>f(*c)
__莱恩打电话来了
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
记忆者
>>>#顺便说一句,`list(the_range)`也有同样的问题。
>>>名单(c)
__莱恩打电话来了
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
记忆者
因此,我的问题是:

  • 为什么CPython调用len(iterable)
  • ?从我看来,只有迭代并抛出迭代器,才能知道迭代器的长度。这是优化吗

  • \uuuu len\uuuu
    方法能否返回对象的“假”长度(即不是内存中元素的真实数量)

  • 为什么CPython调用len(iterable)?从这个问题中,我知道只有迭代并抛出迭代器,才能知道迭代器的长度。这是优化吗

    当python(假设python3)执行
    f(*c)
    时,使用操作码
    调用函数\u EX

     0 LOAD_GLOBAL              0 (f)
     2 LOAD_GLOBAL              1 (c)
     4 CALL_FUNCTION_EX         0
     6 POP_TOP
    
    由于
    c
    是一个iterable,调用
    PySequence\u Tuple
    将其转换为一个Tuple,然后调用
    PyObject\u LengthHint
    来确定新的Tuple长度,因为
    \u len\u
    方法是在
    c
    上定义的,它被调用,其返回值用于为新的Tuple分配内存,由于
    malloc
    失败,最终引发了
    MemoryError
    错误

    /* Guess result size and allocate space. */
    n = PyObject_LengthHint(v, 10);
    if (n == -1)
        goto Fail;
    result = PyTuple_New(n);
    
    \uuuu len\uuuu
    方法能否返回对象的“假”长度(即不是内存中元素的真实数量)

    在这种情况下,是的


    \uuuu len\uuuu
    的返回值小于需要时,python将在填充元组时调整新元组对象的内存空间以适应需要。如果它大于需要,尽管python会分配额外的内存,但最终会调用
    \PyTuple\u Resize
    来回收分配过多的空间。

    。无论如何,我建议不要这样做(使用一个不产生范围元素的
    \uuuuuuuuu iter\uuuuuuu
    )。如果迭代器产生两个项,那么序列的长度是两个。@juanpa.arrivillaga@Ryan我知道
    list(it)
    f(*it)
    都创建一个序列,并将调用
    操作符.length\u hint(it)
    来预分配空间。和
    operator.length\u hint
    查看
    it
    具有
    \uu len\uu
    方法,因此只返回
    len(it)
    ——因此序列分配太大。是这样吗?
    /* Guess result size and allocate space. */
    n = PyObject_LengthHint(v, 10);
    if (n == -1)
        goto Fail;
    result = PyTuple_New(n);