python - 尝试覆盖__getattr__以动态添加类,但只有一些工作

时间:2012-11-30 02:00:47

标签: python class dynamic getattr

我正在尝试在运行时向现有类添加新类 时间(使用“type(...)”)。我也试图覆盖它 新课'__getattr__,这样我就可以做自己的行为了 不属于新类的属性。例如, 我有类foo,我添加类“工具”,我想要foo.tool.test 做我自己的事。下面的代码有效但只是部分代码。如果我显式调用__getattr__,它可以工作(参见第一次打印) 但是当我引用foo.tool.test时,我的被覆盖的__getattr__没有被调用,并且引发了一个属性错误。

非常感谢您的帮助。

class Foo(object):
    def __init__(self):
        self.NameList=[]
        # add new class to ourself
        self.tool = type('tool', (object,), {} )
        # override new class' __getattr__ with call to ourself
        setattr(self.tool, "__getattr__", self.__getattr__ )
        # add one well known test name for now
        self.NameList.append( "test" )

    # should be called by our newly added "tool" object but is only called sometimes...
    def __getattr__(self, attr):
        # print( "__getattr__: %s" % attr )
        if( attr in self.NameList ):     
            return( 99 )
        raise AttributeError("--%r object has no attribute %r" % (type(self).__name__, attr))       

foo = Foo()
# access tool class attribute "test" - it should be seen by the override __getattr__
# the following works...
print( "foo.tool.__getattr__=%d" % foo.tool.__getattr__("test") )  
# but the following does not - why is this not the same as the line above???
print( "foo.tool.test=%d" % foo.tool.test )                         

1 个答案:

答案 0 :(得分:6)

Python在实例的基础中寻找像__getattr__这样的特殊方法 __dict__ s,不在实例__dict__中。

self.tool是一个班级。因此,self.tool.test会调用__getattr__类的self.toolobject) - 这不是我们想要发生的事情。

相反,将self.tool设为一个实例,其类具有__getattr__

class Foo(object):
    def __init__(self):
        self.NameList=[]
        # add new class to ourself
        toolcls = type('tool', (object,), { '__getattr__' : self.__getattr__, } )
        self.tool = toolcls()
        self.NameList.append( "test" )

    # should be called by our newly added "tool" object but is only called sometimes...
    def __getattr__(self, attr):
        # print("__getattr__: (%s, %s)" % (self.__class__.__name__, attr) )
        if( attr in self.NameList ):     
            return( 99 )
        raise AttributeError("--%r object has no attribute %r" % (
            type(self).__name__, attr)) 

foo = Foo()
print( "foo.tool.__getattr__=%d" % foo.tool.__getattr__("test") )
print( "foo.tool.test=%d" % foo.tool.test )    

产量

foo.tool.__getattr__=99
foo.tool.test=99

另外,请注意,如果Foo的实例未定义self.NameList,则上述代码可能会导致无限递归。请参阅Ned Batchelder's post on this suprising pitfall

为了防止无限递归,请使用

def __getattr__(self, attr):
    # print("__getattr__: (%s, %s)" % (self.__class__.__name__, attr) )
    if attr == 'NameList':
        raise AttributeError()
    if( attr in self.NameList ):     
        return( 99 )
    raise AttributeError("--%r object has no attribute %r" % (
        type(self).__name__, attr))