Python __index__ 和求值顺序

huangapple go评论63阅读模式
英文:

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 之后。因此,评估顺序大致如下:

  1. data[i:i(3)]
  2. 评估 i(3) 为 3
  3. 构建切片 slice(i,3)
  4. 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:

  1. data[i:i(3)]
  2. Evaluate i(3) to 3
  3. Build the slice slice(i,3)
  4. Inside data.__getitem__, the integer value of i is needed, so i.__index__ is called, which returns 3.

You can see this by disassembling the indexing operation:

&gt;&gt;&gt; import dis
&gt;&gt;&gt; dis.dis(&#39;data[i:i(3)]&#39;)
  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).

huangapple
  • 本文由 发表于 2020年1月7日 01:01:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/59616123.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定