50

From reading the docs, I understand exactly what getattr() and setattr() do. But it also says explicitly that getattr(x, 'foobar') is equivalent to x.foobar and setattr(x, 'foobar', 123) is equivalent to x.foobar = 123.

So why would I use them?

temporary_user_name
  • 35,956
  • 47
  • 141
  • 220
  • 17
    If you can't think of a good reason to use them, you probably don't have one. :) But one day, you've find yourself with an attribute name in a string variable (e.g., from user input, or computed by processing some JSON from a server, or whatever) and want to get the attribute with the name given in that variable, and that's when you'll use it. – abarnert Oct 01 '13 at 18:55
  • Related: https://stackoverflow.com/questions/75531642/when-is-it-necessary-to-define-getattr-and-setattr-methods-for-a-cla – Lover of Structure Feb 22 '23 at 11:12

7 Answers7

70

Because you can use a dynamic variable too:

somevar = 'foo'
getattr(x, somevar)

You can't do that with regular attribute access syntax.

Note that getattr() also takes an optional default value, to be returned if the attribute is missing:

>>> x = object()
>>> getattr(x, 'foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'foo'
>>> getattr(x, 'foo', 42)
42

Using getattr() you can pull the attribute name from something else, not a literal:

for attrname in dir(x):
    print('x.{} = {!r}'.format(attrname, getattr(x, attrname))

or you can use setattr() to set dynamic attributes:

for i, value in enumerate(dynamic_values):
    setattr(i, 'attribute{}'.format(i), value)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 3
    Good explanation. But it's worth noting that often, if you think you want this, you don't; the attribute should have been a value in a dictionary rather than an attribute in the first place. – abarnert Oct 01 '13 at 18:56
  • @abarnert: Not always. There are usecases to handle dynamic transformations on existing objects. Or looking up attributes that could be missing. – Martijn Pieters Oct 01 '13 at 18:57
  • @MartijnPieters: Yes, that's why I said "often". If they were _never_ useful, they wouldn't be in the language at all. As a good rule of thumb: If you normally want to access something statically, but occasionally dynamically, use an attribute with occasional `getattr`; if you normally want to access it dynamically, but occasionally statically, use a dict with occasional literal keys. – abarnert Oct 01 '13 at 19:00
  • In my current project codebase I count 12 modules using `getattr()` (not counting tests); 4 of those are façades; the rest use `getattr()` to handle optional attributes, returning a default value if missing. – Martijn Pieters Oct 01 '13 at 19:05
  • 1
    @MartijnPieters: I've actually got more `__getattr__` instances in my code than `getattr`… but yes, I use it too. The first time I want `getattr` somewhere, I stop and ask myself whether the thing really should be an attribute. Sometimes the answer is "yes" (or, occasionally, "no, but there's too much spaghetti at this point to fix it…"). But it's always worth thinking about it (except in the cases where it's obvious—e.g., you're writing reflective code for debugging). – abarnert Oct 01 '13 at 19:09
  • @MartijnPieters "In my current project codebase I count 12 modules", as a point of reference, what type of code is it? As a developer for one-off software for scientific projects I've never had to use it so I would be interested to see where it comes up in a live setting. – Hooked Oct 01 '13 at 19:15
  • actually, i would argue that attributes are really useful for one other reason: you can tab-complete them in the console. if you're mostly using python interactively, then this is a big win. but usually, though, if i do that, i try to implement a pattern where both the attributes and `__getitem__` are both defined, so x.attr and x['attr'] are identical. – Corley Brigman Oct 01 '13 at 19:50
  • @Hooked: An enterprise security product central management package built on Django. – Martijn Pieters Oct 01 '13 at 20:07
13

You use them if the attribute you want to access is a variable and not a literal string. They let you parameterize attribute access/setting.

There's no reason to do getattr(x, 'foobar'), but you might have a variable called attr that could be set to "foobar" or "otherAttr", and then do getattr(x, attr).

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
7

Another case probably shows that they are not totally identical:

class A:
    def __init__(self):
        self.__var = 10
        setattr(self, '__var2', 100)

a = A()
print(a.__var2)
# 100
print(a.__var)
# AttributeError: 'A' object has no attribute '__var'

At least, setattr is not identical to ..

Hou Lu
  • 3,012
  • 2
  • 16
  • 23
2

I find it most useful when there is a possibility that the object whose attribute you need might be None. Exemplified below;

obj = None
attr_val = getattr(obj, 'anyvar', None) #This is not an error.
2

There is a difference between setattr and ., not documented yet:

class X:

    def __init__(self, value1, value2):
        self.__non_private_name_1 = value1
        setattr(self, '__non_private_name_2', value2)


>>> x = X('Hi', 'Bye')
>>> x.__dict__
{'_X__non_private_name_1': 'Hi', '__non_private_name_2': 'Bye'}

The latter one, when used to set a dunder value (ones with double underscores) adds a single underscore _ + self.__class__.__name__(i.e. X) to the beginning of the string in right side of . and sets the attribute named with the resulting string(i.e. _X__non_private_name_1).

S.Khajeh
  • 327
  • 1
  • 9
0

i think python has deep thinking to support setattr() and getattr() on instance.

Imaging that you have an instance like a baby, who knows nothing when it is born. i.e. there is no any attributes in that instance. As long as the baby grows up, he/she learns more and more skills, so that setattr() is used to make it work, and it makes no one identical even if all the baby instances are created from the same class.

0

I ran into another use case that requires the use of setattr: decorating imported recursive functions. If you implement the recursive computation of the Fibonacci numbers in a file fibo.py:

def fib(n):
    return n if n < 2 else fib(n-1) + fib(n-2)

and you then want to take advantage of lru_cache in main.py when you import that function, then the straightforward way that works within fibo.py does not work in main.py:

from fibo import fib
from functools import lru_cache
fib = lru_cache(fib)
print(fib(42))  # takes ages

The setattr way works however:

import fibo
from functools import lru_cache
setattr(fibo, "fib", lru_cache(fibo.fib))
print(fibo.fib(42))  # lightning fast

EDIT: well, it turns out that the straightforward way works too if you import the whole module:

import fibo
from functools import lru_cache
fibo.fib = lru_cache(fibo.fib)
print(fibo.fib(42))  # lightning fast
Anthony Labarre
  • 2,745
  • 1
  • 28
  • 39