8

Is there any difference between this code, which uses dot notation for attribute access:

def get_foo(self):
    try:
        return self._attribute
    except AttributeError:
        self._attribute = 'something'
        return self._attribute

And the following code, which uses the getattr function:

def get_foo(self):
    try:
        return getattr(self, '_attribute')
    except AttributeError:
        self._attribute = 'something'
        return self._attribute

They seem to behave the same way to me, but I came across the latter example in some code and I was curious why one would opt for calling getattr() in this case.

larsks
  • 277,717
  • 41
  • 399
  • 399

3 Answers3

4

According to the documentation, they are equivalent (unless a third argument is provided to suppress the AttributeError when the attribute is not present).

Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example, getattr(x, 'foobar') is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised.

I can't think of many reasons to prefer getattr over normal dot notation here . . . The only time I can think where I might be tempted to use the former is if I was trying to prevent my linter from complaining about protected-access (which is probably best disabled with a pragma if you have a legitimate reason for doing it). . .

mgilson
  • 300,191
  • 65
  • 633
  • 696
2

These are equivalent, however getattr can take an optional default value to be returned when the attribute does not exist, e.g.

getattr(self, '_attribute', None)

Thus, the second function can alternatively be written without try/catch (assuming self._attribute can't be None):

def get_foo(self):
    attr = getattr(self, '_attribute', None)
    if attr is None:
        self._attribute = 'something'
    return attr
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • 3
    That's not quite the same -- the original function will preserve a value of `None`, but your version will replace it with `'something'`. – Ismail Badawi Dec 23 '15 at 20:55
2

As the other answers provide, the two are functionally the same thing without the third argument.

One trivial difference is that they do not have the same effect on the stack; this is at best a code-golfy difference, and could only come up in very contrived scenarios (perhaps testing to see if you were in "debug" mode and providing different timings, if you were a malicious QA person).

import inspect

class Foo:

    def __init__(self):
        self._x = 3

    @property
    def x(self):
        curframe = inspect.currentframe()
        calframe = inspect.getouterframes(curframe, 2)      
        print(calframe[1][2])
        return self._x

f = Foo()
def bar():
    print( f.x )    
    # if instead we use the print statement on the next line,
    # we note that there is one more stack frame. In no universe
    # is this particularly impact, but in a closed test case
    # we would be able to determine which one was being used
    #print( getattr(f,'x') )

bar()
en_Knight
  • 5,301
  • 2
  • 26
  • 46