【问题标题】:Why is `x[i]` not equivalent to `x.__getitem__(x)`?为什么 `x[i]` 不等于 `x.__getitem__(x)`?
【发布时间】:2021-09-21 13:07:25
【问题描述】:

来自documentation

x[i] 大致相当于type(x).__getitem__(x, i)

与拥有看似简单的x.__getitem__(i)相比,上述方法有什么好处?
编辑:为什么 Python 会这样?

作为标准行为的一个缺点,让我展示这个示例代码,我惊讶地发现最后一个断言失败,而倒数第二个断言(直接调用 __getitem__)通过了。

def poww_bar(base):
    class Bar():
        def __getitem__(self, x):
            return lambda: base**x
    return Bar()

def poww_foo(base):
    class Foo():
        pass
    f = Foo()
    f.__getitem__ = lambda x: lambda: base ** x
    return f

pow_bar2 = poww_bar(2)
pow_foo2 = poww_foo(2)


assert pow_bar2.__getitem__(3)() == 8 # OK
assert pow_bar2[3]() == 8 # OK

assert pow_foo2.__getitem__(3)() == 8 # OK
assert pow_foo2[3]() == 8 # TypeError: 'Foo' object is not subscriptable

【问题讨论】:

  • 它失败可能是因为定义索引运算符不仅仅是f.__getitem__ = ...。也许当你做def __getitem__(...): ... 时,解释器会在幕后做一些魔术来将此函数注册为特殊的并且有资格使用索引语法糖
  • 此外,您的f.__getitem__ 版本仅属于Foo 的单个实例f,而不是整个类(它接受1 个参数,与标准版本中的(self, x) 相比)
  • @AlexeyLarionov 似乎 OP 意识到了这一点,并在问 为什么 Python 会这样。

标签: python magic-methods syntactic-sugar


【解决方案1】:

方法是属性,而不是实例属性。

没有名为__getitem__ 的实例属性与pow_bar2 关联。因此查找继续检查该名称的属性的类,并成功找到Bar.__getitem__

但过程并没有就此结束。 pow_bar2.__getitem__(i) 不等同于 Bar.__getitem__(i),因为 Python 首先检查属性查找会生成一个实现 descriptor protocol 的对象。由于Bar.__getitem__function 的一个实例,它确实实现了描述符协议。

接下来的步骤不是返回函数本身,而是返回Bar.__dict__['__getitem__'].__get__(pow_bar2, Bar) 的结果。 (我改用Bar.__dict__ 来强调我们不会陷入触发描述符协议的无限循环。)这是method 的一个实例,它本身就是一个可调用的,传递的是自己的参数,连同pow_bar2,作为原始函数的参数。

因此,pow_bar2.__getitem__(i) 等价于Bar.__dict__['__getitem__'].__get__(pow_bar2, Bar)(i),大致等价于Bar.__dict__['__getitem__'](pow_bar2, i)


但实际上,pow_bar2[i] 只是比pow_bar2.__getitem__(i) 更短且更容易识别(由于数十年来其他语言对此语法的支持)。 __getitem__ 使得 [] 的使用可扩展到其他类,而不是将其限制为内置类型。


描述符协议不仅仅是一种使实例方法行为看起来比必要更复杂的一次性功能。它还决定了类方法、静态方法和属性的工作方式,并可进一步用于以其他方式自定义属性行为。

【讨论】:

    【解决方案2】:

    这可能只是一种优化。类函数在类定义中只有一个引用。对象函数在每个对象中都有一个引用。所以__getitem__方法被指定为一个类函数,所以他们不需要浪费时间去寻找它的对象定义。

    当然,这都是猜测。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 2021-07-11
      • 2013-03-08
      • 2023-03-19
      • 1970-01-01
      • 2021-01-19
      • 2019-12-02
      相关资源
      最近更新 更多