例如,我收到了if
声明:
if user.address.streetname == 'Target':
pass
elif:
(...)
else:
(...)
但并非所有用户都有足够的资金来获得地址,因此可能会引发异常
在我的例子中,django DoesNotExist
例外。在这种情况下,它应该假设为假。
如果elif else流动,如何在这个地方处理异常而不破坏?
答案 0 :(得分:3)
如果user.address
是模型实例,则可以执行
if user and user.address and user.address.streetname and user.address.streetname == 'Target':
#Do something/
或者,您也可以这样做:
address = getattr(user, 'address', None) if user else None
if address and getattr(address, 'streetname', '') == 'Target':
#do something
答案 1 :(得分:1)
使用duck typing并创建一个sentinel对象,该对象保证使用不匹配的streetname
属性来代替不可用的user.address
。
poor_user_address = type('', (), {'streetname': None})()
if getattr(user, 'address', poor_user_address).streetname == "Target":
...
对type
的调用会创建一个带有类变量streetname
的最小类;课程的其他细节是无关紧要的。使用duck typing,poor_user_address
是不同类的实例并不重要,只要它表现出相同的行为即可。在这种情况下,唯一预期的行为是具有可以与“目标”进行比较的streetname
属性。
答案 2 :(得分:0)
Here's another stackoverflow question that answers this
相关部分将是:hasattr(用户,'地址')
如果在访问该属性之前将其添加到if,则可以维护if / else流。
答案 3 :(得分:0)
try:
streetname = user.address.streetname
except DoesNotExist:
streetname = None
# or:
# streetname = NonexistenceSentinel()
if streetname == 'Target':
pass
elif:
...
else:
...
但是你可能正在寻找的是一些语法糖,让你不要把它放在任何地方。这是一个可以让你这样做的食谱:
# 'Stupid' object that just returns itself.
# Any attribute will just return itself.
class SentinelObject(object):
__slots__ = []
def __init__(self):
pass
def __getattr__(self, key):
return self
def __nonzero__(self):
return False
def delegate_specials(specials):
specialnames = ['__%s__'%s for s in specials.split()]
def wrapit(cls, method):
return lambda self, *args, **kwargs: getattr(self._original_obj, method)(*args, **kwargs)
def dowrap(cls):
for n in specialnames:
setattr(cls, n,
wrapit(cls, n))
return cls
return dowrap
@delegate_specials('getitem setitem iter add sub mul div repr str len')
class SafeLookupObject(object):
__slots__ = ['_original_obj', '_sentinel_default']
__original_names__ = ['_original_obj', '_sentinel_default']
def __init__(self, original_obj, sentinel_default=SentinelObject()):
self._original_obj = original_obj
self._sentinel_default = sentinel_default
def __getattr__(self, key):
if key in self.__original_names__:
return object.__getattr__(self, key)
else:
try:
val = getattr(self._original_obj, key)
if callable(val):
return val
else:
return SafeLookupObject(val, self._sentinel_default)
except AttributeError:
return self._sentinel_default
def __setattr__(self, key, value):
if key in self.__original_names__:
return object.__setattr__(self, key, value)
else:
return setattr(self._original, key, value)
可能不完美,第一次通过看起来不错。
这是做什么的:传入一个原始对象和一个默认值。默认值是一个特殊的SentinelObject(一分钟内更多)。仅适用于getattr,如果它不存在,则返回sentinel值。如果它确实存在,它会检查它是否可以调用。如果它是可调用的(即函数),则直接返回它。如果没有,它将它包装在SafeLookupObject中并返回它。
目的是,如果要查找x.y.z
,可以将x
包装在SafeLookupObject中,然后x.y
也将自动包装,一直向下,因此,如果它在列表中的任何地方失败,那么该值将被替换为sentinel对象。
因此特殊的SentinelObject,它为你传入的任何属性返回自己。这与上面的内容使它完全递归。
即。假设您使用a.b.c.d
查找a
是安全的。 a.b
没问题,但b中不存在c
。默认设置为None
,a.b.c
会返回None
,但a.b.c.d
会引发异常。
如果使用SentinelObject作为默认值,则a.b.c
将返回SentinelObject,它们都是布尔值False,并且可以匹配以确定不存在的属性。 <{1}}也返回相同的SentinelObject,因此它现在完全安全。