英文:
Python __index__ and evaluation order
问题
I was expecting the two code below to return the same result because of https://docs.python.org/3.7/reference/expressions.html#evaluation-order. However it looks like __index__ from Counter class is called after __call__ in the second case. Is it because __index__ gets called only when reaching ] ? Or is there another explanation ?
class Counter:
def __init__(self, start=0):
self._i = start
def __call__(self, step:int=None):
if step is not None:
self._i += step
return self._i
def __index__(self):
return self._i
data = list(range(0, 10))
i = Counter(0)
data[i():i(3)]
returns [0, 1, 2]
i = Counter(0)
data[i:i(3)]
returns []
PS: The goal of the Counter class is to allow assignment like syntax in python < 3.8
英文:
I was expecting the two code below to return the same result because of https://docs.python.org/3.7/reference/expressions.html#evaluation-order. However it looks like __index__ from Counter class is called after __call__ in the second case. Is it because __index__ gets called only when reaching ] ? Or is there another explanation ?
<!-- language: lang-python -->
class Counter:
def __init__(self, start=0):
self._i = start
def __call__(self, step:int=None):
if step is not None:
self._i += step
return self._i
def __index__(self):
return self._i
data = list(range(0, 10))
i = Counter(0)
data[i():i(3)]
returns [0, 1, 2]
i = Counter(0)
data[i:i(3)]
returns []
PS: The goal of the Counter class is to allow assignment like syntax in python < 3.8
i = 0
data[i: (i := i+3)]
答案1
得分: 2
data[i:i(3)] 等同于 data.__getitem__(slice(i, i(3)));i.__index__ 只有在切片的起始元素实际需要时才会被调用,在 data.__getitem__ 函数体内,但在 i(3) 已经将 i._step 增加到 3 之后。因此,评估顺序大致如下:
data[i:i(3)]- 评估
i(3)为 3 - 构建切片
slice(i,3) - 在
data.__getitem__内部需要i的整数值,所以调用了i.__index__,返回值为 3。
您可以通过反汇编索引操作来验证这一点:
import dis
dis.dis('data[i:i(3)]')
首先,调用了 i(3)(位于偏移量 8),然后构建了切片(位于偏移量 10),最后调用了 data.__getitem__(位于偏移量 12)。
英文:
data[i:i(3)] is equivalent to data.__getitem__(slice(i, i(3))); i.__index__ doesn't get called until the starting element of the slice is actually needed, inside the body of data.__getitem__, but after i(3) has already incremented i._step to 3. So the order of evaluation is something like:
data[i:i(3)]- Evaluate
i(3)to 3 - Build the slice
slice(i,3) - Inside
data.__getitem__, the integer value ofiis needed, soi.__index__is called, which returns 3.
You can see this by disassembling the indexing operation:
>>> import dis
>>> dis.dis('data[i:i(3)]')
1 0 LOAD_NAME 0 (data)
2 LOAD_NAME 1 (i)
4 LOAD_NAME 1 (i)
6 LOAD_CONST 0 (3)
8 CALL_FUNCTION 1
10 BUILD_SLICE 2
12 BINARY_SUBSCR
14 RETURN_VALUE
First i(3) is called (at offset 8), then the slice is built (at offset 10), and finally data.__getitem__ is called (at offset 12).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论