Python - Дубликаты в dir () Это ошибка? - PullRequest
3 голосов
/ 22 января 2020

В следующей последовательности с помощью numpy dir (np) возвращает повторяющиеся записи. Это ошибка? Разрешено ли / dir () возвращать дубликаты?

Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> len(dir(np))
620
>>> np.testing
<module 'numpy.testing' from 'C:\\Python\\Python38\\lib\\site-packages\\numpy\\testing\\__init__.py'>
>>> len(dir(np))
621
>>> [i for i in dir(np) if i == "testing"]
['testing', 'testing']
>>> np.__version__
'1.18.1'
>>>

1 Ответ

4 голосов
/ 22 января 2020

Похоже, что это (относительно безвредная) ошибка, вызванная оптимизацией numpy, вставленной во избежание затрат на импорт подпакета testing, в то же время предоставляя testing и Tester в качестве атрибутов root numpy package.

Оптимизация использует уровень модуля __getattr__ (доступен только на Python 3.7+, поэтому он не используется на 3.6 и более ранних версиях), чтобы импортировать их, только если они явный доступ (в этот момент testing становится реальным атрибутом numpy, поскольку дочерние модули и пакеты присоединяются к своим родителям в качестве атрибутов при импорте автоматически), но чтобы продолжать притворяться, что они импортированы с нетерпением, он также определяет модуль -level __dir__, который притворяется, что они уже существуют:

def __dir__():
    return list(globals().keys()) + ['Tester', 'testing']

Недостаток в том, что, если numpy.testing импортируется (либо явно, либо неявно через хук __getattr__), то он уже появляется в globals(), поэтому добавление ['Tester', 'testing'] к list добавляет вторую копию 'testing' к результату dir.

Они могут тривиально исправить это путем дедупликации (до преобразования в list или просто без преобразования, поскольку dir уже задокументировано для автоматического выполнения преобразования ), а не конкатенации после, например:

def __dir__():
    return globals().keys() | {'Tester', 'testing'}

но это не серьезная ошибка; код, который ломается из-за того, что dir дает удвоенный результат, вероятно, довольно хрупок и глючит из get- go.

Полное объяснение этой оптимизации приведено в комментариях источника:

    # Importing Tester requires importing all of UnitTest which is not a
    # cheap import Since it is mainly used in test suits, we lazy import it
    # here to save on the order of 10 ms of import time for most users
    #
    # The previous way Tester was imported also had a side effect of adding
    # the full `numpy.testing` namespace
    #
    # module level getattr is only supported in 3.7 onwards
    # https://www.python.org/dev/peps/pep-0562/

На 3.6 и более ранних версиях путь к коду, определяющий __getattr__ и __dir__, пропускается, и все, что он делает, это:

    # We don't actually use this ourselves anymore, but I'm not 100% sure that
    # no-one else in the world is using it (though I hope not)
    from .testing import Tester

, что означает, что testing и Tester являются "настоящими" атрибутами из get- go, и ошибка не возникает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...