哪一个是检查属性存在的最好方法?
哪一个更好的方法来检查属性的存在?
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
,但是这是一个遥不可及的场景而且你需要非常小心线程,我不认为这三个差异值得担心。
使用hasattr
比try/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的方式
如果您只需要一个默认值, getattr
是try/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
结论
在内部, getattr
和hasattr
内build的只是使用try/except
技术(除了写在C中)。 所以他们在重要的方面都performance得相同,select正确的是由于环境和风格的问题。
try/except
EAFP代码try/except
的try/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