wim*_*wim 33 python list python-internals
class Foo:
def __getitem__(self, item):
print('getitem', item)
if item == 6:
raise IndexError
return item**2
def __len__(self):
print('len')
return 3
class Bar:
def __iter__(self):
print('iter')
return iter([3, 5, 42, 69])
def __len__(self):
print('len')
return 3
Run Code Online (Sandbox Code Playgroud)
演示:
>>> list(Foo())
len
getitem 0
getitem 1
getitem 2
getitem 3
getitem 4
getitem 5
getitem 6
[0, 1, 4, 9, 16, 25]
>>> list(Bar())
iter
len
[3, 5, 42, 69]
Run Code Online (Sandbox Code Playgroud)
为什么list打电话__len__?它似乎没有使用任何明显的结果.一个for循环不会做.迭代器协议中的任何地方都没有提到这个,它只谈及__iter__和__next__.
这个Python预先为列表预留空间,还是像这样聪明的东西?
(Linux上的CPython 3.6.0)
Jim*_*ard 35
请参阅PEP 424中的"基本原理"部分,该部分介绍__length_hint__并提供有关动机的见解:
能够根据估计的预期大小预先分配列表
__length_hint__可以是重要的优化.已经观察到CPython比PyPy更快地运行一些代码,纯粹是因为存在这种优化.
除此之外,文档还object.__length_hint__验证了这纯粹是一种优化功能:
被叫实施
operator.length_hint().应返回对象的估计长度(可能大于或小于实际长度).长度必须是整数>= 0.这种方法纯粹是一种优化,从来不需要正确性.
所以__length_hint__在这里,因为它可以导致一些很好的优化.
PyObject_LengthHint,首先尝试从object.__len__ (如果已定义)获取值,然后尝试查看是否object.__length_hint__可用.如果两者都不存在,则返回8列表的默认值.
listextend,list_init根据Eli在他的回答中所说的,根据这个PEP修改,为任何定义a __len__或a的东西提供优化__length_hint__.
>>> bytes(Foo())
len
getitem 0
...
b'\x00\x01\x04\t\x10\x19'
Run Code Online (Sandbox Code Playgroud)
这样做的bytearray目的,但是,只有当你extend他们:
>>> bytearray().extend(Foo())
len
getitem 0
...
Run Code Online (Sandbox Code Playgroud)
和tuple创建中间序列以填充自己的对象:
>>> tuple(Foo())
len
getitem 0
...
(0, 1, 4, 9, 16, 25)
Run Code Online (Sandbox Code Playgroud)
如果有人在徘徊,为什么在课前准确'iter'打印,而不是在课后发生: 'len'BarFoo
这是因为如果手头的对象定义了__iter__ Python 将首先调用它来获取迭代器,从而运行它print('iter').如果它回归使用,也不会发生同样的情况__getitem__.
Eli*_*sky 31
list是一个列表对象构造函数,它将为其内容分配一个初始内存片.列表构造函数通过检查传递给构造函数的任何对象的长度提示或长度,尝试找出该初始内存片的大小.请在此处查看PyObject_LengthHintPython 源代码中的调用.这个地方是从列表构造函数中调用的 - list_init
如果您的对象没有__len__或__length_hint__,那没关系 - 使用默认值8 ; 由于重新分配,它可能效率较低.
| 归档时间: |
|
| 查看次数: |
1769 次 |
| 最近记录: |