为啥重新定义 __getattr__() 的对象会抛出 TypeError?
Posted
技术标签:
【中文标题】为啥重新定义 __getattr__() 的对象会抛出 TypeError?【英文标题】:Why does an object with redefined __getattr__() throws TypeError?为什么重新定义 __getattr__() 的对象会抛出 TypeError? 【发布时间】:2019-07-23 18:04:48 【问题描述】:这里是代码
class MyTest:
def __init__(self):
pass
def __getattr__(self, attr):
pass
t = MyTest()
print 'my test object: %r' %t
所以打印会触发TypeError: 'NoneType' object is not callable
,而我只想查看对象是否存在。
授予此代码不是很有用。但是我在一个大代码库中有一个像这样的存根类,所以我做到了
if module and module.class and module.class.propery:
# do something with that property
...
并得到了Type Error: 'NoneType' object is not callable
,但该线路没有调用任何东西!我猜python是在幕后隐式调用一些函数。
奇怪的是,如果该类继承自 Object
,则不会发生这种情况
发生了什么事?
【问题讨论】:
__getattr__
应该返回一些东西,而不是 None
@Jean-FrançoisFabre 歧视! None
是“某物”!
您的字符串格式化操作尝试访问t
的__repr__
属性,并且由于您使用的是旧式类(您没有从object
继承),__getattr__
正在返回 None
而不是实际返回要调用的所需方法。不要使用老式的类。
【参考方案1】:
在旧式类中,__getattr__
用于更多种类的属性访问,包括魔术方法。 %
运算符试图调用 t.__repr__()
以填充 %r
占位符,但 t.__repr__
由 t.__getattr__('__repr__')
评估,返回 None
。
在if
的情况下,调用了不同的魔法方法,但出现了同样的问题。
>>> class Foo:
... def __getattr__(self, attr):
... print(attr)
...
>>> f = Foo():
>>> if f:
... pass
__nonzero__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
使用新型类,__getattr__
仅在无法通过常规方法找到属性时调用(检查实例的 __dict__
属性或实例的 MRO 中任何类的属性)。
>>> class Foo(object):
... def __init__(self):
... self.x = 3
... def __getattr__(self, attr):
... print(attr)
...
>>> f = Foo()
>>> if f:
... pass
...
>>> f.x
3
>>> f.y
y
在if f
的情况下,f
本身没有实现__nonzero__
或__len__
,其父级object
也没有实现,但在这种情况下,没有使用任何属性;事实上,f
是一个对象。在f.x
中,x
是在实例的属性dict中找到的,所以直接返回它的值。只有y
(未由f
、Foo
或object
另行定义)调用对__getattr__
的调用。
【讨论】:
so `if t` 也调用__repr__()
?
不,但是在布尔上下文中评估 t
会调用一些东西。使用 Jean-François Fabre 的建议,即在返回任何内容之前将参数打印到 __getattr__
,应该表明调用 t.__nonzero__
以确定 t
是否为真。【参考方案2】:
在带有老式类的 python 2 中,当您尝试在对象上调用 __repr__
(打印时)时,您会调用 __getattr__
。
由于你暴力地存根这个方法,它返回None
并且python 尝试调用None
(因为它期望返回一个方法)
尝试拨打object.__getattr__
,这样就可以了:
class MyTest:
def __init__(self):
pass
def __getattr__(self, attr):
print(attr) # so we see why getattr is called
return object.__getattr__(self,attr) # so it doesn't crash (neither it is useful...)
t = MyTest()
print ('my test object: %r' %t)
打印:
__repr__
my test object: <__main__.MyTest instance at 0x00000000031B3808>
这是一个特定的 python 2/旧样式对象问题。 Python 3 或新型对象的行为不同
【讨论】:
以上是关于为啥重新定义 __getattr__() 的对象会抛出 TypeError?的主要内容,如果未能解决你的问题,请参考以下文章
面向对象高级之 __getattr__,__setattr__,__delattr__
__setattr__和__delattr__和__getattr__
python cookbook第三版学习笔记十二:类和对象创建新的类或实例属性
python自定义属性访问__setattr__, __getattr__, __setattribute__