Python 2和3中“dir”的区别

9
以下代码在Python 2和Python 3中的行为不同,我不确定原因。
class Dataset(object):
    def __getattr__(self, item):
        if not item in dir(self):
            print(item)

a = Dataset()
a.Hello

Python 3 的结果如下:

> Hello

Python 2 的结果:

__members__
__members__
__methods__
...

无限递归直至达到递归层数的区别。"dir" 的行为有什么不同?

编辑:是否有解决方法?self.dict 是明显的选择,但它不包括函数,这在我的代码中是一个问题。


2
实际上,如果属性已经存在,__getattr__()根本不应该被调用(参见https://docs.python.org/2/reference/datamodel.html#object.__getattr__)。 - dhke
3个回答

6
Python 2.7和3.5版本中dir的文档似乎是相同的,没有实现细节。但显然,在Python 2中调用dir()会引发__getattr__导致无限递归。但两个文档都说:

因为dir()主要作为交互提示符使用的便利性而提供,它试图提供一组有趣的名称,而不是严格或一致地定义一组名称,并且其详细行为可能会在发布版本之间更改。例如,当参数是类时,元类属性不在结果列表中。

这个关于方便性的说明很重要。

如果您将__getattr__修改为查看self.__dict__而不是使用dir(),问题就会消失。
In [5]: class Dataset(object):
          def __getattr__(self, item):
            if not item in self.__dict__:
              print(item)
   ...:             

In [6]: a = Dataset()

In [7]: a.Hello
Hello

1
直到我回答完之后才看到有关函数的编辑。 - kdopen
1
据我所知,dir() 尝试查找 __dir__。由于它不存在,因此会调用 __getattr__()。然后再调用 dir()... - dhke

3

没有检查源代码,我无法说清楚 为什么 会发生这种情况(尽管我有一些假设),但这里有一个相当简单的解决方法:

class Dataset(object):
    def __getattr__(self, item):
        try:
            super(Dataset, self).__getattr__(item)
        except AttributeError:
            print(item)

1
不需要在def __getattr__内检查if not item in dir(self) 如果item在dir(self)中列出,__getattr__不会被调用。 代码可以是:
class Dataset(object):
    x = 12
    def __getattr__(self, item):
        print(item)

a = Dataset()
a.Hello  # print Hello
a.x      # return 12

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接