检查属性是否存在的最佳方法是哪种?

And*_*ran 64 python attributes

哪种方法可以检查属性是否存在?

Jarret Hardie提供了这个答案:

if hasattr(a, 'property'):
    a.property
Run Code Online (Sandbox Code Playgroud)

我看到它也可以这样做:

if 'property' in a.__dict__:
    a.property
Run Code Online (Sandbox Code Playgroud)

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

Die*_*Epp 141

没有"最好"的方法,因为你不只是检查一个属性是否存在; 它总是一些大型计划的一部分.有几种正确的方法和一种值得注意的错误方法.

错误的方法

if 'property' in a.__dict__:
    a.property
Run Code Online (Sandbox Code Playgroud)

这是一个演示,显示此技术失败:

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
Run Code Online (Sandbox Code Playgroud)

输出:

'prop' in a.__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()
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

此外,用于将字符串转换为整数.

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

甚至导入可选模块......

try:
    import readline
except ImportError:
    pass
Run Code Online (Sandbox Code Playgroud)

LBYL的方式

hasattr当然,这种方法也有效.这种技术被称为"在你跳跃之前看",或简称为LBYL.

# Cached attribute
if not hasattr(self, 'big_object'):
    big_object = CreateBigObject()
    self.big_object = CreateBigObject()
big_object.do_something()
Run Code Online (Sandbox Code Playgroud)

(hasattr内置实际上在3.2之前的Python版本中就异常而言表现得很奇怪 - 它会捕获它不应该的异常 - 但这可能是无关紧要的,因为这种异常是不可能的.这种hasattr技术也比它慢try/except,但是你不要经常调用它,并且差别不是很大.最后,hasattr不是原子的,所以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)
Run Code Online (Sandbox Code Playgroud)

而LBYL有时是完全错误的:

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

如果你想编写一个用于导入可选模块的LBYL函数,请成为我的客人...听起来这个函数将是一个完整的怪物.

getattr方式

如果您只需要一个默认值,getattr则是一个较短的版本try/except.

x = getattr(self, 'x', default_value)
Run Code Online (Sandbox Code Playgroud)

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

x = getattr(self, 'attr', None)
if x is None:
    x = CreateDefaultValue()
    self.attr = x
Run Code Online (Sandbox Code Playgroud)

或者如果None是可能的值,

sentinel = object()

x = getattr(self, 'attr', sentinel)
if x is sentinel:
    x = CreateDefaultValue()
    self.attr = x
Run Code Online (Sandbox Code Playgroud)

结论

在内部,getattrhasattr内置只使用try/except技术(除了用C编写).因此,它们的行为方式与它的重要性相同,而选择正确的方式则取决于环境和风格.

try/exceptEAFP代码总是会擦一些程序员走错了路,和hasattr/getattrLBYL代码会激怒其他程序员.它们都是正确的,并且通常没有真正令人信服的理由选择其中一个.(然而其他程序员感到厌恶的是你认为属性未定义是正常的,而且一些程序员感到震惊,甚至可能在Python中有一个未定义的属性.)

  • 可以说,当你几乎总是希望`x`具有属性`x.attr`时,`try ... except`会更好.(那么异常将是非常特殊的.) (4认同)
  • 我几乎总是使用`try ...除了'个人,'hasattr`方法在我的嘴里留下了不好的味道. (3认同)

jfs*_*jfs 11

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)
Run Code Online (Sandbox Code Playgroud)

not_exist如果在您的情况下更合适,您可以使用默认值而不是sentinel.

我不喜欢try: do_something(x.attr) \n except AttributeError: ..它可能隐藏AttributeError在内部do_something()功能.

*在Python 3.1 hasattr()禁止所有异常(不仅仅是AttributeError)之前,getattr()应该使用它.


iTa*_*ayb 5

hasattr()是Pythonic的做法.学习它,喜欢它.

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

if varName in locals() or in globals():
    do_something()
else:
    do_something_else()
Run Code Online (Sandbox Code Playgroud)

我个人不喜欢捕捉异常以便检查一些东西.它看起来很丑陋.它与检查字符串是否仅包含数字的方式相同:

s = "84984x"
try:
    int(s)
    do_something(s)
except ValueError:
    do_something_else(s)
Run Code Online (Sandbox Code Playgroud)

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