Что произошло внутри во время распаковки? - PullRequest
0 голосов
/ 28 июня 2018

Я пытаюсь выяснить, какие методы вызываются при распаковке dict, чтобы я мог настроить процесс?

Полагаю, что следующий хак показал бы методы, вызываемые при любом доступе к методу Dict экземпляров:

class Dict(dict):
    def __getattribute__(self, name):
        print(name)
        return super().__getattribute__(name)

Но следующий сеанс показывает, что не вызывается метод для выполнения распаковки dict?

In [1]: d = Dict(a=1)
__class__
__class__

In [2]: {**d}
Out[2]: {'a': 1}

Так что на самом деле здесь происходит? Как мне настроить процесс распаковки?


Редактировать

Не думаю, что вопрос является дубликатом другого . Даже при реализации всех специальных методов, упомянутых в ответах на этот вопрос, ни один из них не вызывается во время распаковки.

In [66]: class Dict(dict):
    ...:     def __getattribute__(self, name):
    ...:         print(name)
    ...:         return super().__getattribute__(name)
    ...:     def keys(self):
    ...:         print("hello")
    ...:         return super().keys()
    ...:     def __getitem__(self, key):
    ...:         print("hello")
    ...:         return super().__getitem__(key)
    ...:     def __len__(self):
    ...:         print("hello")
    ...:         return super().__len__()
    ...:     def __iter__(self):
    ...:         print("hello")
    ...:         return super().__iter__()
    ...:     

In [67]: d = Dict(a=1)
__class__
__class__

In [68]: {**d}
Out[68]: {'a': 1} 

Вы можете видеть, что никакая строка print не называется вообще. Поэтому мой вопрос остается без ответа.

FWIW, версия python - Python 3.6.5.

1 Ответ

0 голосов
/ 08 ноября 2018

Это была ошибка в том, как Python обрабатывал dict подклассы, которая была исправлена ​​в конце сентября 2018 года .

До исправления любой подкласс dict был преобразован в обычный dict с использованием конкретных dict -специфических методов C API (которые пропускают все динамически определенные переопределения). После исправления код проверяет, был ли переопределен __iter__ (ну, эквивалент C, tp_iter), и если это так, он не использует быстрый путь для dict s. Проверка __iter__ немного неверна IMO (единственные два метода, которые она на самом деле использует , это keys и __getitem__), но если вы переопределяете keys, вы, вероятно, должны переопределить __iter__ кроме того, так что это не составляет больших трудностей (во многих случаях один может быть псевдонимом другого или, самое большее, тонкой оболочкой, в зависимости от того, возвращает ли keys итератор или объект представления).

Учитывая, как давно было исправление, вам нужно обновить Python, чтобы получить выгоду. 3.6.7 и 3.7.1 - это первые микро-релизы в соответствующих строках второстепенных версий, в которые включено исправление, поэтому обновление до любой из них должно заставить ваш код работать.

...