这个令我感到困惑。考虑以下Django模型 - 代表动物园管理员和他们负责清洁的动物园笼子:
class Zookeeper(moodels.Model):
name = models.CharField(max_length=40)
class Cage(models.Model):
zookeeper = models.ForeignKey(Zookeeper)
现在假设我想将接收器连接到Cage
的{{1}}信号:
post_init
正如预期的那样,这会引发异常,因为@receiver(models.signals.post_init, sender=Cage)
def on_cage_init(instance, **kwargs):
print instance.zookeeper
尚未分配给Cage
。考虑对接收者身体的以下修改:
Zookeeper
可以预期这会打印“No Zookeeper”,因为尚未将其分配给实例。相反,会引发异常:
print getattr(instance, 'zookeeper', 'No Zookeeper')
为什么会引发异常?如果该属性不存在,Traceback (most recent call last):
File "../zoo/models.py", line 185, in on_cage_init
print getattr(instance, 'zookeeper', 'No Zookeeper')
File "/usr/local/lib/python2.7/dist-packages/django/db/models/fields/related.py", line 324, in __get__
"%s has no %s." % (self.field.model.__name__, self.field.name))
DoesNotExist: Cage has no zookeeper.
是否应该返回提供的默认值?我可以证明该属性不存在:
getattr()
...打印print hasattr(instance, 'zookeeper')
。
答案 0 :(得分:6)
该类很可能已定义__getattribute__
。见这个例子:
>>> class O(object):
... def __getattribute__(self, name):
... raise Exception("can't get attribute")
...
>>> o = O()
>>> getattr(o, 'test', 'nothing')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __getattribute__
Exception: can't get attribute
请注意getattr
在内部如何实质上调用o.__getattribute__
,如果这会引发一般异常,那么它只会因该异常而失败。
但是,如果正确定义以引发AttributeError
,则getattr
会正确捕获。
>>> class O(object):
... def __getattribute__(self, name):
... raise AttributeError("can't get attribute")
...
>>> o = O()
>>> getattr(o, 'test', 'nothing')
'nothing'
因此,这可能会被视为DoesNotExist
例外定义中的一个错误,它不能正确地从AttributeError
继承。
一个更完整的例子来演示以上所有内容:
>>> class O(object):
... def __getattribute__(self, name):
... if name == 'test':
... return 'good value'
... elif name == 'bad':
... raise Exception("don't raise this")
... else:
... raise DoesNotExist()
...
>>> class DoesNotExist(AttributeError):
... pass
...
>>> o = O()
>>> getattr(o, 'test', 'nothing')
'good value'
>>> getattr(o, 'something', 'nothing')
'nothing'
>>> getattr(o, 'bad', 'nothing')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __getattribute__
Exception: don't raise this
当然,上述所有内容并不能完全帮助您解决该问题。而不是等待解决该错误,只需实现陷阱该异常的getattr
(或您可能期望的任何其他异常)。这样的事情可能有用:
def safe_getattr(obj, name, default):
try:
return getattr(obj, name, default)
except Exception: # or your specific exceptions
return default
答案 1 :(得分:5)
@ metatoaster的解释非常好,这基本上就是发生了什么。请参阅__get__
魔法定义here。
作为解决方案,我会应用"Easier to ask for forgiveness than permission"原则。尝试获取属性并捕获特定的异常:
from django.core.exceptions import ObjectDoesNotExist
try:
print instance.zookeeper
except ObjectDoesNotExist:
print "No zookeeper"