即使属性存在,getattr()是否始终调用现有的__getattr __()?

时间:2015-10-24 16:58:33

标签: python python-2.7

是真的吗?

我在下一段代码中遇到了困难。我希望稍后在execute()调用中调用一些方法。但令人惊讶的是,当我调用__getattr__而不是首先搜索方法__m1和__m2时,python会调用默认方法getattr(self,...)

class MyApi(object):
    "Class with API."

    def __init__(self):
        self.methods = []


    class Callable(object):
        def __init__(self, obj, name):
            self.obj = obj
            self.name = name            

        def __call__(self, *args, **kwargs):            
            self.obj.methods.append((self.name, args, kwargs,))


    def __m1(self, name):
        print "m1: name is %s" % name
        exit(0)


    def __m2(self, *args, **kwargs):
        print "m2: args is {} and kwargs is {}".format(args, kwargs)
        exit(0)


    def execute(self):
        for meth in self.methods:
            meth_name = '__' + meth[0]          
            print meth_name
            getattr(self, meth_name)(*meth[1], **meth[2]) # it seems to call __getattr__, not existing __m1 and __m2

        self.methods = []

    def __getattr__(self, name):
        return MyApi.Callable(self, name)



api = MyApi()

#import pdb; pdb.set_trace()

api.m1('Serg')
api.m2('John', 'Harrison', **{'key1': 1, 'key2': 2})

api.execute()

讨论的结果。

我们同意问题出现在两个下划线中,隐藏属性__m1__m2

2 个答案:

答案 0 :(得分:2)

来自docs

  

当属性查找未在通常位置找到属性时调用(即,它不是实例属性,也不是在类树中找到自己)。   [...]   请注意,如果通过常规机制找到属性,则不会调用__getattr__()

所以不,在正常情况下不应该调用它。

如果您遇到内部方法调用__getattr__的问题,可以始终使用object.__getattr__(self, attr_name)来使用python的默认查找。

如果您遇到此类问题,则可能会遇到name mangling dunder名称的问题。如果一个class属性以 d 两个得分下的开头,那么python将破坏除了该类实例之外的任何访问的名称。这包括儿童班!使用单个下划线表示“私有”方法/属性,除非您确实需要该功能。

答案 1 :(得分:1)

没有

如果存在属性,则不会调用__getattr__

如果您需要此类行为,则必须使用方法__getattribute__

在我看来,你的问题是(正如MisterMiyagi正确指出的那样),方法命名为__m1__m2(顺便说一句:什么是正确的名字 - 在一个位置你在另一个__m1中调用它m1 - 这是不一致的)将被系统破坏。这意味着,系统会将其视为_<classname>__<methodname>_MyApi__m1。因此,使用“getattr”时,必须使用损坏的名称。