英文:
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 ofi
is 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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论