哪一个是检查属性存在的最好方法?

哪一个更好的方法来检查属性的存在?

Jarret Hardie提供了这个答案:

if hasattr(a, 'property'): a.property 

我看到这也可以这样做:

 if 'property' in a.__dict__: a.property 

一种方法通常比其他方法更有用吗?

没有“最好”的方法,因为你永远不会检查是否存在一个属性; 它总是一些更大的程序的一部分。 有几个正确的方法和一个值得注意的错误的方法。

错误的方法

 if 'property' in a.__dict__: a.property 

这是一个示范,显示这种技术失败:

 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 

输出:

 “。prop”在.__ dict__ = False
 hasattr(a,'prop')= True
 a.prop = 3

大多数时候,你不想乱用__dict__ 。 这是做特殊事情的一个特殊属性,检查一个属性是否存在是相当普通的。

EAFP的方式

Python中一个常见的习惯用法是“容易要求宽恕而不是许可”,简称EAFP。 你会看到很多使用这个习惯用法的Python代码,而不仅仅是检查属性的存在。

 # 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() 

请注意,这与打开可能不存在的文件完全相同。

 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 

此外,将string转换为整数。

 try: i = int(s) except ValueError: print "Not an integer! Please try again." sys.exit(1) 

即使导入可选模块…

 try: import readline except ImportError: pass 

LBYL的方式

hasattr方法当然也起作用。 这种技术被称为“先看你跳跃”,简称LBYL。

 # Cached attribute if not hasattr(self, 'big_object'): big_object = CreateBigObject() self.big_object = CreateBigObject() big_object.do_something() 

hasattr内build实际上在3.2之前的Python版本中performanceexception – 它会捕获它不应该的exception – 但这可能是不相关的,因为这样的exception是不可能的hasattr技术也比try/except ,但是你不会经常调用它,而且差别不是很大。最后, hasattr不是primefaces的,所以如果另一个线程删除了属性,它可能会抛出AttributeError ,但是这是一个遥不可及的场景而且你需要非常小心线程,我不认为这三个差异值得担心。

使用hasattrtry/except要简单得多,只要你需要知道的是该属性是否存在。 对我来说,最大的问题是LBYL技术看起来很奇怪,因为作为Python程序员,我更习惯于阅读EAFP技术。 如果您重写上述示例以便使用LBYL样式,则会得到代码笨拙,完全不正确或难以编写。

 # 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有时是完全错误的:

 if os.path.isfile('some_file'): # At this point, some other program could # delete some_file... f = open('some_file', 'r') 

如果你想写一个LBYL函数来导入可选模块,那么我的客人…听起来像这个函数将是一个完全的怪物。

getattr的方式

如果您只需要一个默认值, getattrtry/except的较短版本。

 x = getattr(self, 'x', default_value) 

如果构造的默认值是昂贵的,那么你最终会得到这样的结果:

 x = getattr(self, 'attr', None) if x is None: x = CreateDefaultValue() self.attr = x 

或者,如果“ None是可能的值,

 sentinel = object() x = getattr(self, 'attr', sentinel) if x is sentinel: x = CreateDefaultValue() self.attr = x 

结论

在内部, getattrhasattr内build的只是使用try/except技术(除了写在C中)。 所以他们在重要的方面都performance得相同,select正确的是由于环境和风格的问题。

try/except EAFP代码try/excepttry/except总是会以一种错误的方式揉搓一些程序员,并且hasattr/getattr LBYL代码会使其他程序员感到厌烦。 他们都是正确的,通常没有真正令人信服的理由来挑选其中之一。 (然而其他程序员却反感,你认为一个属性是未定义的,一些程序员感到震惊,甚至有可能在Python中有一个未定义的属性。)

hasattr()*的方式。

a.__dict__是丑陋的,在许多情况下它不起作用。 hasattr()实际上试图获取属性并在内部捕获AttributeError所以即使定义了自定义的__getattr__()方法,它也能正常工作。

为避免请求属性两次,可以使用getattr()的第三个参数:

 not_exist = object() # ... attr = getattr(obj, 'attr', not_exist) if attr is not_exist: do_something_else() else: do_something(attr) 

你可以使用默认值而不是not_exist sentinel,如果它更适合你的情况。

我不喜欢try: do_something(x.attr) \n except AttributeError: ..它可能会隐藏do_something()函数内的AttributeError

* 在Python 3.1之前hasattr()抑制了所有exception (不仅仅是AttributeError ),如果它不是所希望的getattr()应该被使用。

hasattr()是Pythonic的做法。 学习它,爱它。

其他可能的方法是检查variables名是否在locals()globals()

 if varName in locals() or in globals(): do_something() else: do_something_else() 

我个人很讨厌为了检查某些东西而接收exception。 它看起来感觉很难看。 这与检查一个string是否只包含数字是一样的:

 s = "84984x" try: int(s) do_something(s) except ValueError: do_something_else(s) 

而不是轻轻地使用s.isdigit() 。 好恶。

非常古老的问题,但它确实需要一个很好的答案。 即使是一个简短的程序,我会说使用自定义函数!

这是一个例子。 这对于所有的应用程序来说都不是完美的,但是它是为了parsing来自无数的API和使用Django的响应。 很容易解决每个人的需求。

 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, 'xyz', 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