Python 3.4 __getattr__被调用的属性(应该?)存在

时间:2015-04-01 19:25:59

标签: python python-3.x

我有以下课程,意在模仿dict而不是实际上是一个子类:

class DB:

    def __getattribute__(self, name):
        print('__getattribute__("%s")' % name)
        if name.startswith('__') and name not in ['__init__', '__new__',
                '__dict__', '__getattribute__', '__getattr__', '__setattr__',
                '__delattr__', '__doc__', '__module__', '__del__', '__hash__']:
            return eval('self._d.%s' % name)
        print('it is not being diverted to self._d')
        raise AttributeError(name)

    def __getattr__(self, name):
        print('__getattr__("%s")' % name)
        if name.startswith('__'):
            raise AttributeError(name)
        return eval('self._d.%s' % name)

    def __setattr__(self, name, val):
        if name == '_d':
            self.__dict__[name] = val
        else:
            return self._d.__setattr__(name, val)

    def __delattr__(self, name):
        if name == '_d':
            del self.__dict__[name]
        else:
            return self._d.__delattr__(name)

    def __init__(self):
        self._d = {}

使用时会产生以下输出:

>>> d = DB()
__getattribute__("__dict__")
it is not being diverted to self._d
__getattr__("__dict__")
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    d = DB()
  File "<pyshell#1>", line 31, in __init__
    self._d = {}
  File "<pyshell#1>", line 20, in __setattr__
    self.__dict__[name] = val
  File "<pyshell#1>", line 15, in __getattr__
    raise AttributeError(name)
AttributeError: __dict__

它不应该永远不会到达__getattr__("__dict__"),因为self.__dict__因为这是一个对象而应该已经存在吗?

我在Python 2.6中使用了几个月的程序中成功使用了这个对象(好吧,一个本质上完全相同的对象),我正在尝试升级到Python 3.4。根据我在网上可以找到的一切,Python 3应该像Python 2一样对待它。

编辑:this other question类似,但该问题涉及__getattr__根本没有被调用。不过,两者显然都与新式和旧式的课程有关。

1 个答案:

答案 0 :(得分:2)

您重新覆盖__getattribute__ - 这是属性查找的默认实现 - 以查找NameError时提升__dict__的方式。这会触发对__getattr__的调用,从而引发AttributeError。结果是完全您实际要求的内容(如果不是您所期望的)。

FWIW:

  1. 作为一般规则,请勿使用eval("self.%s" % name),请使用getattr(obj, name)
  2. 不要覆盖__getattribute__(也不是__setattr__),除非您真的了解您正在做的事情
  3. 如果你真的来覆盖它们(提示:这样做的理由很少),委托默认实现(使用super())。
  4. 另外:你发布的代码没有工作&#34;按原样#34;在Python 2.7中(对self._d.__setattr__的调用引发了一个AttributeError,并且使DB成为一个新式的类,与Python 3.x完全相同。