首页 > 解决方案 > Python - dir() 中的重复是错误吗?

问题描述

在以下序列中,使用 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'
>>>

标签: pythonnumpy

解决方案


这看起来是一个(相对无害的)错误,由优化引发,numpy以避免testing急切地导入子包的费用,同时仍然提供testingTester作为根numpy包的属性。

优化使用模块级别__getattr__(仅在 Python 3.7+ 上可用,因此在 3.6 及更早版本上不使用)仅在显式访问它们时才导入它们(此时testing成为 的真实属性numpy,因为子模块和包是作为属性自动附加到它们的父级),但是为了继续假装它们被急切地导入,它还定义了一个模块级__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由于产生双倍结果而中断的代码可能从一开始就非常脆弱和错误。

此优化的完整解释在源注释中:

    # 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是从一开始的“真实”属性,并且不会出现错误。


推荐阅读