__getattribute__与__getattr__

Python·语法 2018-12-15 2085 字 13 浏览 点赞

参加校招的时候被面试官问到了,没能答上,主要是混淆了__getattribute__和__getattr__两个魔法方法。因此这里笔记一下。

首先我们看看如何访问一个对象的属性:

class A(object):
    def __init__(self):
        self.name = "Bob"
        self.age = 18
        self.gender  = "male"

if __name__ == "__main__":
    a = A()
    print(a.name)
    print(a.age)
    print(a.gender)

# 输出:
#Bob
#18
#male

如果我们想改变访问属性的逻辑,如执行a.age语句并非返回18,而是返回问年龄是不礼貌的行为。这里就可以用__getattribute__方法拦截属性,实现我们想要实现的逻辑。

class A(object):
    def __init__(self):
        self.name = "Bob"
        self.age = 18
        self.gender  = "male"

    def __getattribute__(self, attr):
        # 拦截age属性
        if attr == "age":
            return "问年龄是不礼貌的行为"
        # 非age属性执行默认操作
        else:
            return object.__getattribute__(self, attr)


if __name__ == "__main__":
    a = A()
    print(a.age)
    print(a.name)
    print(a.gender)

# 输出:
#问年龄是不礼貌的行为
#Bob
#male

事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()),都会自动去调用__getattribute__方法。但是,如果某个属性在__getattribute__方法中未能找到,此时会调用__getattr__方法。

如我们访问对象a中不存在的属性,会得到异常。比方执行print(a.NAME)语句,运行结果如下:
AttributeError: 'A' object has no attribute 'NAME'

但我就想无论是a.NAMEa.Name,还是a.NaME等等,即n a m e(包含大小写)四个元素组成的属性,访问结果都同a.name一样,可以做如下处理:

class A(object):
    def __init__(self):
        self.name = "Bob"
        self.age = 18
        self.gender  = "male"

    def __getattr__(self, attr):
        return eval("self."+attr.lower()) #即:再次去执行__getattribute__方法
    

if __name__ == "__main__":
    a = A()
    print("a.name -> {}".format(a.name))
    print("a.NAME -> {}".format(a.NAME))
    print("a.Name -> {}".format(a.Name))
    print("a.NaME -> {}".format(a.NaME))

# 输出:
#a.name -> Bob
#a.NAME -> Bob
#a.Name -> Bob
#a.NaME -> Bob

总结

1.__getattribute__方法优先级比__getattr__

2.只有在__getattribute__方法中找不到对应的属性时,才会调用__getattr__

3.如果是对不存在的属性做处理,尽量把逻辑写在__getattr__方法中

4.如果非得重写__getattribute__方法,需要注意两点:第一是避免.操作带来的死循环;第二是不要遗忘父类的__getattribute__方法在子类中起的作用



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论