在学习python的类知识的时候,总是看到各种带__
的属性、方法,其中上次也总结了这些带下划线的成员的知识点。这里面有一些我感到特别的好奇,所以也做一下总结。
__getattr__
和getattr
我首先感到好奇的类当中的反射。在一个定义的类当中,可以用getattr(object, name, [,defalut])
来获取object
对象中的name
属性,同时也有的使用object.__getattr__(self, name)
来获取object
的name
属性。
这两个长得那么像,总感觉他们是一个东西啊。于是我开始进一步去了解后,发现实际上他们两个,还真不是一个东西。getattr
是python
自带的全局函数,而__getattr__
是一个需要用户在类中定义以后才可以用的方法。这里就需要搞清楚python自带的全局函数和这种需要用户在类中自定义的方法的区别是什么?
python自带的全局函数是python自带的,随时所有人都可以用,在哪里都可以用;而需要用户在类中定义的方法(也叫钩子方法)则是用户定义了就有,没定义就按python默认的走,有点类似用户定义了,解释器就会在适当的时机下去触发它。所以他们本质上是不一样的。
以getattr
和__getattr__
来举例:
python
class A:
pass
class B:
def __getattr__(self, name):
print(f'正在访问{self.name}')
return self.name
a = A()
b = B()
print(a.x)
# -> AttributeError: 'A' object has no attribute 'x'
# class A没有定义__getattr__钩子方法,所有解释器就会抛出异常
print(b.x)
# -> 正在访问x
# -> x
# class B定义了__getattr__钩子方法,所有这里解释器就去触发了钩子方法,返回了x
print(getattr(a, 'x', 'default a'))
# -> default a
print(getattr(a, 'x'))
# -> AttributeError: 'A' object has no attribute 'x'
print(getattr(b, 'x', 'default b'))
# -> 正在访问x
# -> x
# 这里就体现了getattr和__getattr__的联系,下面用一个图来体现他们俩的联系
getattr
和__getattr__
的联系:

也不是所有的钩子函数都类似__getattr__
和getattr
它俩这样的关系,比如len()
和__len__
它们俩就与之不一样。len()也是python内置内置的全局函数,但它需要在类中定义了__len__
才可以使用,平常我们使用len(1)
、len([1,2,3])
可以用,是因为在int
类、str
类、list
类中已经定义了__len__
方法了。
python
class A:
pass
class B:
def __len__(self):
return 42
class C:
def __len__(self):
return "42"
a = A()
b = B()
c = C()
len(a)
# --> TypeError: object of type 'A' has no len()
# 虽然len是内置函数,但是它是去调用__len__这个钩子方法,如果用户定义了这个钩子方法,这个len才可以正常使用,没定义,就不能正常使用
# 这个和getattr这个内置函数就有一些区别
# getattr内置函数是,不管定义还是没定义,都可以使用,只是如果定义了__getattr__这个钩子方法,getattr会多调用一步
print(len(b))
# --> 42
print(len(c))
# --> TypeError: 'str' object cannot be interpreted as an integer
# 注意这里报错,是因为内置函数len要求__len__必须返回非负整数,而自己定义的__len__返回了一个字符串,所以报错
从上面两个例子来看,内置函数和钩子方法有点像是python的制造大师们,把内置函数开发好了,提供给开发者们用,但是也配置了一些使用方法,有的内置函数使用方法上要配合一些钩子函数来使用,而这个内置函数对钩子函数的使用方式又各有不同。
__getattr__
和__getattribute__
除了有内置函数getattr
和__getattr__
很像以外,还有另外一个方法__getattribute__
也和它非常像,那它们俩又是什么关系呢?他们俩都是python中的魔术方法,但__getattr__
是用户定义了才有,而__getattribute__
是在python的基类object
类中定义好的,不管用户定义还是不定义,它都存在,只是如果用户定义了,就会覆盖__getattribute__
。所以如果说钩子方法是用户定义才有,没定义就没有的话,__getattribute__
就不算是一个钩子方法了。
那么他们俩在功能上有什么联系?我也用一个图来进行总结。这个图中访问类中的属性,可以是通过object.xxx
的形式来访问,也可以是通过getattr(object,'x',)
的方式来访问。
注意 :如果
getattr
中提供了默认值,那么在调用__getattribte__
以后如果没有返回值,那么就直接去getattr
的默认值了,不会再去调用__getattr__
了。