__getattribute__ 和 __getattr__ 的区别

2019/06/21 Python

__getattribute____getattr__ 的区别

这两个方法在外形上有点相似,但是差距不小,后面的 getattr() 和 setattr()、delattr()类似于Java里的 setter,getter。

__getattribute__ 是啥子

__getattribute__ 是新式类才出现的。

Python Version 必要条件 类类型
<2.2 经典类
(2.2, 2.7) 经典类
(2.2, 2.7) 继承object 新式类
3.x 新式类

__getattribute__ 是属性访问拦截器,就是当这个类的属性被访问时,会自动调用类的 __getattribute__ 方法。

Python中只要是新式类,就默认存在属性拦截器,只不过是拦截后没有进行任何操作,而是直接返回。所以我们可以自己改写 __getattribute__ 方法来实现相关功能,比如查看权限、打印 log 日志等。如下代码,简单理解即可:

class Student(object):
    def __init__(self, name, num):
        self.name = name
        self.num = num

    def __getattribute__(self, *args, **kwargs):
        if args[0] == "name":
            print("you want get attr({}),but will return ".format(args[0]))
            return str(object.__getattribute__(self,*args,**kwargs)) + "1"
        else:
            print("you don't want get attr name")
            return object.__getattribute__(self,*args,**kwargs)

s1 = Student("Ash", 1)
s2 = Student("Light", 2)
print(s1.name)
print(s2.name)
print(s1.num)
print(s2.num)

上述过程中如果访问的是 name 字段,就会打印日志,并且将返回值变为字符串并且后缀加上字符“1”。

初学者用__getattribute__方法时,容易栽进这个坑,什么坑呢,直接看代码:

class Student(object):
    def __init__(self, name, num):
        self.name = name
        self.num = num

    def __getattribute__(self, *args, **kwargs):
        if args[0] == "name":
            print("you want get attr({}),but will return ".format(args[0]))
            return str(object.__getattribute__(self,*args,**kwargs)) + "1"
        else:
            print("you don't want get attr name")
            return self.special_call()
    
    def special_call(self):
        return "special call"

s1 = Student("Ash", 1)
print(s1.name)
print(s1.num)

上面执行的结果就会进入到错误的无限递归,这是因为,当调用 s1.num 的时候,会去调用 self.special_call, 但是调用 self.special_call 的时候又会从 __getattribute__ 走,无限递归。

如果 __getattribute__ 没有定义好,这个问题会出现在很多地方,包括对象的内建方法或属性,比如 __dict__,所以这时候就推荐使用 __getattr__

__getattr__ 是啥子

当访问某个实例属性时, __getattribute__ 会被无条件调用。

如果没有实现自己的 __getattr__ 方法,就会抛出 AttributeError 提示找不到这个属性。

如果自定义了自身的 __getattr__ 方法的话,方法会在这种找不到属性的情况下被调用。

所以在找不到属性的情况下通过实现自定义的 __getattr__ 方法来实现一些功能是一个不错的方式。

class Test(object):
    def __init__(self, p):
        self.p = p

    def __getattr__(self, item):
        return 'default'

t = Test('p1')
print(t.p)
print(t.p2)

__getattr__ 同一级别的还有 __setattr____delattr__ 方法。

Search

    Table of Contents