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
语句的一部分。
要记住 del
和 return
之类的东西不是函数(并避免意外的优先级意外),最好不要在 "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
在 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
语句的一部分。
要记住 del
和 return
之类的东西不是函数(并避免意外的优先级意外),最好不要在 "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