python del 函数如何在不调用 __getitem__ 的情况下工作

How does the python del function work without calling __getitem__

在 python 中,一个对象在 class 定义了一个

时是可订阅的
__getitem__(self, k)

方法,允许我们通过括号语法"get items":

obj[k]

内置 del 函数的语法是:

del(obj[k])

这将从(可订阅的)对象 obj 中删除项目 k。但显然,没有调用特殊的 getitem 方法。

参见下面的示例

>>> class A:
...     def __init__(self, d):
...         self._d = d
...
...     def __getitem__(self, k):
...         print("Calling __getitem__")
...         return self._d[k]
...
...     def __delitem__(self, k):
...         del(self._d[k])
...
>>>
>>> a = A({1: 'one', 2: 'two', 3: 'three'})
>>> a._d
{1: 'one', 2: 'two', 3: 'three'}
>>> a[2]
Calling __getitem__
'two'
>>> del(a[2])
>>> # Note there was no "Calling __getitem__" print!

所以好像在a[2]将工作转发给getitem方法之前,解释器知道del上下文,并绕过它,直接调用

a.__delitem__(2)

相反。

这是如何运作的?

最重要的是:这个机制可以定制吗?

例如,我可以写一个函数 foo 以便

foo(obj[k])

从不打电话

obj.__getitem__(k)

而是,例如,

obj.foo(k)

del 不是 函数。它可以做到这一点,因为它不是一个功能。这就是为什么它不是函数。它是语言内置的关键字,作为 del 语句的一部分。

要记住 delreturn 之类的东西不是函数(并避免意外的优先级意外),最好不要在 "argument" 周围加上括号:

del whatever

而不是

del(whatever)

del 不获取对象并将其删除。 del 右边的东西不是要求值的表达式。它是一个target_list,与赋值语句中=左侧出现的语法相同:

target_list     ::=  target ("," target)* [","]
target          ::=  identifier
                     | "(" [target_list] ")"
                     | "[" [target_list] "]"
                     | attributeref
                     | subscription
                     | slicing
                     | "*" target

要删除 订阅 目标,如 obj[k],Python 计算表达式 obj 和表达式 k 以生成两个对象,然后调用第一个对象的 __delitem__ 方法,第二个对象作为参数。 obj[k] 永远不会被评估为表达式,尽管它的一部分是。


这一切都依赖于编译器和语法的支持,对于任意的用户自定义函数是做不到的。

您正在编写示例代码,就好像 del 是一个函数 参数并假设参数 a[2] 必须首先通过 __getitem__()。但严格来说,del是一个statement。这意味着语言解析器可以用一种特殊的方式对待它 方式——换句话说,不一定像函数调用。

我们可以使用 dis 包来获得一些提示。注意 del 操作直接表示为非常具体的 DELETE_SUBSCR 操作。 它绕过了 len 示例使用的 BINARY_SUBSCR 步骤。

from dis import dis

def f(xs):
    del xs[2]

def g(xs):
    len(xs[2])

print('\n# del')
dis(f)
print('\n# len')
dis(g)

输出(汇总):

# del
0 LOAD_FAST                0 (xs)
2 LOAD_CONST               1 (2)
4 DELETE_SUBSCR
6 LOAD_CONST               0 (None)
8 RETURN_VALUE

# len
 0 LOAD_GLOBAL              0 (len)
 2 LOAD_FAST                0 (xs)
 4 LOAD_CONST               1 (2)
 6 BINARY_SUBSCR
 8 CALL_FUNCTION            1
10 POP_TOP
12 LOAD_CONST               0 (None)
14 RETURN_VALUE