Как работает функция python del без вызова __getitem__ - PullRequest
0 голосов
/ 21 марта 2019

В Python объект подписывается, когда его класс определяет метод

__getitem__(self, k)

, что позволяет нам "получать элементы" с помощью синтаксиса скобок:

obj[k]

Синтаксис длявстроенная функция del:

del(obj[k])

Это удаляет элемент k из (подписного) объекта obj.Но, по-видимому, без вызова специального метода 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!

Таким образом, кажется, что перед тем как [2] перенаправить работу в метод getitem, интерпретатор знаетконтекст del и обходит его, вызывая вместо него

a.__delitem__(2)

.

Как это работает?

И, самое главное: настраивается ли этот механизм?

Могу ли я, например, написать функцию foo, чтобы

foo(obj[k])

никогда не вызывал

obj.__getitem__(k)

, а вместо этого, например,

obj.foo(k)

Ответы [ 2 ]

2 голосов
/ 21 марта 2019

del - это , а не функция. Это можно сделать, потому что это не функция. Это , почему это не функция. Это ключевое слово, встроенное в язык как часть оператора del.

Чтобы иметь в виду, что такие вещи, как del и return, не являются функциями (и избегают неожиданных неожиданностей предшествования), лучше не ставить круглые скобки вокруг «аргумента»:

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] никогда не оценивается как выражение, хотя его части.


Все это зависит от поддержки компилятора и грамматики и не может быть выполнено для произвольных пользовательских функций.

1 голос
/ 21 марта 2019

Вы пишете свой пример кода, как если бы del была функцией, принимающей аргументы и предполагая, что аргумент a[2] должен быть обработан сначала через __getitem__(). Но, строго говоря, del это утверждение . Это означает, что синтаксический анализатор языка может обращаться с ним путь - другими словами, не обязательно как вызов функции.

Мы можем использовать пакет dis , чтобы получить некоторые подсказки по этому поводу. Обратите внимание, как del операция выражается непосредственно как очень специфическая DELETE_SUBSCR операция. Он обходит шаг BINARY_SUBSCR, используемый в примере len.

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
...