元类的__getattr__未被调用

4 投票
1 回答
1732 浏览
提问于 2025-04-18 11:41

正如标题所说的,无论我怎么做,__getattr__ 都不会被调用。我甚至尝试在实例上使用它(虽然这听起来很荒谬),结果也是没有反应。就好像在元类中,__getattr__ 被禁止了一样。

如果有人能给我指个方向,告诉我在哪里可以找到相关的文档,我会非常感激。

代码如下:

class PreinsertMeta(type):

    def resolvedField(self):
        if isinstance(self.field, basestring):
            tbl, fld = self.field.split(".")
            self.field = (tbl, fld)
        return self.field

    Field = property(resolvedField)

    def __getattr__(self, attrname):
        if attrname == "field":
            if isinstance(self.field, basestring):
                tbl, fld = self.field.split(".")
                self.field = (tbl, fld)
            return self.field
        else:
            return super(PreinsertMeta, self).__getattr__(attrname)

    def __setattr__(self, attrname, value):
        super(PreinsertMeta, self).__setattr__(attrname, value)


class Test(object):
    __metaclass__ = PreinsertMeta
    field = "test.field"

print Test.field  # Should already print the tuple
Test.field = "another.field"  # __setattr__ gets called nicely
print Test.field  # Again with the string?
print Test.Field  # note the capital 'F', this actually calls resolvedField() and prints the tuple

感谢 BrenBarn,这里是最终的可用实现:

class PreinsertMeta(type):

    def __getattribute__(self, attrname):
        if attrname == "field" and isinstance(object.__getattribute__(self, attrname), basestring):
            tbl, fld = object.__getattribute__(self, attrname).split(".")
            self.field = (tbl, fld)
        return object.__getattribute__(self, attrname)

1 个回答

8

根据文档__getattr__ 这个方法只有在你要访问的属性不存在时才会被调用。因为你的类里已经有了一个 field 属性,所以 __getattr__ 就不会被触发。如果你真的想要拦截所有属性的访问,可以使用 __getattribute__,不过从你的例子来看,不太清楚你为什么需要这样做。需要注意的是,这和元类没有关系;如果你创建一个普通类的实例并给它一些属性,你也会看到同样的行为。

即使假设你使用了 __getattribute__,也就是说当属性存在时它会被调用,但你的实现其实并不太合理。在 __getattr__ 里面,你试图获取 self.field 的值。但是如果 __getattribute__ 已经被调用了,它在访问这个属性时又会再次被调用,这就会导致无限递归:为了获取 self.field,它必须调用 __getattribute__,而这个调用又会尝试获取 self.field,然后又调用 __getattribute__,如此循环下去。有关 __getattribute__ 的更多信息,可以查看文档,了解如何解决这个问题。

撰写回答