hasattr() vs try-except block to deal with non-existent attributes

前端 未结 12 2212
庸人自扰
庸人自扰 2020-11-30 23:29
if hasattr(obj, \'attribute\'):
    # do somthing

vs

try:
    # access obj.attribute
except AttributeError, e:
    # deal with Attr         


        
相关标签:
12条回答
  • 2020-11-30 23:47

    Any benches that illustrate difference in performance?

    timeit it's your friend

    $ python -mtimeit -s 'class C(object): a = 4
    c = C()' 'hasattr(c, "nonexistent")'
    1000000 loops, best of 3: 1.87 usec per loop
    $ python -mtimeit -s 'class C(object): a = 4
    c = C()' 'hasattr(c, "a")'
    1000000 loops, best of 3: 0.446 usec per loop
    $ python -mtimeit -s 'class C(object): a = 4
    c = C()' 'try:
     c.a
    except:
     pass'
    1000000 loops, best of 3: 0.247 usec per loop
    $ python -mtimeit -s 'class C(object): a = 4
    c = C()' 'try:
     c.nonexistent
    except:
     pass'
    100000 loops, best of 3: 3.13 usec per loop
    $
    
           |positive|negative
    hasattr|  0.446 |  1.87 
    try    |  0.247 |  3.13
    
    0 讨论(0)
  • 2020-11-30 23:55

    I'd suggest option 2. Option 1 has a race condition if some other thread is adding or removing the attribute.

    Also python has an Idiom, that EAFP ('easier to ask forgiveness than permission') is better than LBYL ('look before you leap').

    0 讨论(0)
  • 2020-11-30 23:56

    From a practical point of view, in most languages using a conditional will always be consderably faster than handling an exception.

    If you're wanting to handle the case of an attribute not existing somewhere outside of the current function, the exception is the better way to go. An indicator that you may want to be using an exception instead of a conditional is that the conditional merely sets a flag and aborts the current operation, and something elsewhere checks this flag and takes action based on that.

    That said, as Rax Olgud points out, communication with others is one important attribute of code, and what you want to say by saying "this is an exceptional situation" rather than "this is is something I expect to happen" may be more important.

    0 讨论(0)
  • 2020-11-30 23:58

    If it's just one attribute you're testing, I'd say use hasattr. However, if you're doing several accesses to attributes which may or may not exist then using a try block may save you some typing.

    0 讨论(0)
  • 2020-11-30 23:59

    hasattr internally and rapidly performs the same task as the try/except block: it's a very specific, optimized, one-task tool and thus should be preferred, when applicable, to the very general-purpose alternative.

    0 讨论(0)
  • 2020-11-30 23:59

    I almost always use hasattr: it's the correct choice for most cases.

    The problematic case is when a class overrides __getattr__: hasattr will catch all exceptions instead of catching just AttributeError like you expect. In other words, the code below will print b: False even though it would be more appropriate to see a ValueError exception:

    class X(object):
        def __getattr__(self, attr):
            if attr == 'a':
                return 123
            if attr == 'b':
                raise ValueError('important error from your database')
            raise AttributeError
    
    x = X()
    print 'a:', hasattr(x, 'a')
    print 'b:', hasattr(x, 'b')
    print 'c:', hasattr(x, 'c')
    

    The important error has thus disappeared. This has been fixed in Python 3.2 (issue9666) where hasattr now only catches AttributeError.

    An easy workaround is to write a utility function like this:

    _notset = object()
    
    def safehasattr(thing, attr):
        return getattr(thing, attr, _notset) is not _notset
    

    This let's getattr deal with the situation and it can then raise the appropriate exception.

    0 讨论(0)
提交回复
热议问题