Which is the best way to check for the existence of an attribute?
哪种方法更好地检查属性的存在?
Jarret Hardie提供了以下答案:
1 2 | if hasattr(a, 'property'): a.property |
我看到它也可以这样做:
1 2 | if 'property' in a.__dict__: a.property |
一种方法通常比其他方法更常用吗?
没有"最佳"方法,因为您绝不只是检查属性是否存在;它始终是某个较大程序的一部分。有几种正确的方法和一种明显的错误方法。
错误的方式1 2 | if 'property' in a.__dict__: a.property |
下面是一个演示,说明此技术失败:
1 2 3 4 5 6 7 8 9 | class A(object): @property def prop(self): return 3 a = A() print"'prop' in a.__dict__ =", 'prop' in a.__dict__ print"hasattr(a, 'prop') =", hasattr(a, 'prop') print"a.prop =", a.prop |
输出:
1 2 3 | 'prop' in a.__dict__ = False hasattr(a, 'prop') = True a.prop = 3 |
大多数时候,你不想和
在Python中,一个常见的习语是"请求宽恕比请求允许更容易",简称EAFP。您将看到许多使用这种习惯用法的Python代码,而不仅仅是为了检查属性的存在性。
1 2 3 4 5 6 7 8 9 10 | # Cached attribute try: big_object = self.big_object # or getattr(self, 'big_object') except AttributeError: # Creating the Big Object takes five days # and three hundred pounds of over-ripe melons. big_object = CreateBigObject() self.big_object = big_object big_object.do_something() |
注意,这与打开可能不存在的文件完全相同。
1 2 3 4 5 6 7 8 | try: f = open('some_file', 'r') except IOError as ex: if ex.errno != errno.ENOENT: raise # it doesn't exist else: # it does and it's open |
另外,用于将字符串转换为整数。
1 2 3 4 5 | try: i = int(s) except ValueError: print"Not an integer! Please try again." sys.exit(1) |
甚至导入可选模块…
1 2 3 4 | try: import readline except ImportError: pass |
路比路
当然,
1 2 3 4 5 | # Cached attribute if not hasattr(self, 'big_object'): big_object = CreateBigObject() self.big_object = CreateBigObject() big_object.do_something() |
(对于3.2之前的Python版本,
使用
1 2 3 4 5 6 | # Seems rather fragile... if re.match('^(:?0|-?[1-9][0-9]*)$', s): i = int(s) else: print"Not an integer! Please try again." sys.exit(1) |
而lbyl有时完全不正确:
1 2 3 4 | if os.path.isfile('some_file'): # At this point, some other program could # delete some_file... f = open('some_file', 'r') |
如果你想写一个导入可选模块的lbyl函数,请便…听起来这个功能就像一个怪物。
GATTAL方式如果您只需要一个默认值,那么
1 | x = getattr(self, 'x', default_value) |
如果默认值的构建成本很高,那么您将得到如下结果:
1 2 3 4 | x = getattr(self, 'attr', None) if x is None: x = CreateDefaultValue() self.attr = x |
或者如果
1 2 3 4 5 6 | sentinel = object() x = getattr(self, 'attr', sentinel) if x is sentinel: x = CreateDefaultValue() self.attr = x |
结论
在内部,
方法是用
为了避免两次请求属性,可以使用
1 2 3 4 5 6 7 8 | not_exist = object() # ... attr = getattr(obj, 'attr', not_exist) if attr is not_exist: do_something_else() else: do_something(attr) |
如果您的情况更合适的话,您可以使用默认值而不是
我不喜欢
except AttributeError: ..
*在python 3.1之前,如果不希望使用
另一种可能的方法是检查变量名是否在
1 2 3 4 | if varName in locals() or in globals(): do_something() else: do_something_else() |
我个人不喜欢为了检查某些东西而捕捉异常。它看起来和感觉都很难看。这与检查字符串是否只包含数字相同:
1 2 3 4 5 6 | s ="84984x" try: int(s) do_something(s) except ValueError: do_something_else(s) |
而不是轻轻地使用
这个问题很老,但确实需要一个很好的答案。对于一个短程序,我会说使用自定义函数!
下面是一个例子。它并不适用于所有应用程序,但适用于我的应用程序,用于解析来自无数API的响应并使用django。很容易根据每个人自己的需求进行调整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | from django.core.exceptions import ObjectDoesNotExist from functools import reduce class MultipleObjectsReturned(Exception): pass def get_attr(obj, attr, default, asString=False, silent=True): """ Gets any attribute of obj. Recursively get attributes by separating attribute names with the .-character. Calls the last attribute if it's a function. Usage: get_attr(obj, 'x.y.z', None) """ try: attr = reduce(getattr, attr.split("."), obj) if hasattr(attr, '__call__'): attr = attr() if attr is None: return default if isinstance(attr, list): if len(attr) > 1: logger.debug("Found multiple attributes:" + str(attr)) raise MultipleObjectsReturned("Expected a single attribute") else: return str(attr[0]) if asString else attr[0] else: return str(attr) if asString else attr except AttributeError: if not silent: raise return default except ObjectDoesNotExist: if not silent: raise return default |