What Is Getattr() Exactly and How to Use It

What is getattr() exactly and how do I use it?

getattr(object, 'x') is completely equivalent to object.x.

There are only two cases where getattr can be useful.

  • you can't write object.x, because you don't know in advance which attribute you want (it comes from a string). Very useful for meta-programming.
  • you want to provide a default value. object.y will raise an AttributeError if there's no y. But getattr(object, 'y', 5) will return 5.

getattr on class objects

Is this good enough?

import types
class Test(object):
@staticmethod
def foo():
print 'foo'

def bar(self):
print 'bar'

In combination with:

>>>(isinstance(getattr(Test, 'foo'), types.FunctionType),
isinstance(getattr(Test, 'bar'), types.FunctionType))
True, False

You can also use the inspect module:

>>> inspect.isfunction(Test.foo)
True
>>> inspect.isfunction(Test.bar)
False

With a little additional work you can even distinguish class methods from instance methods and static methods:

import inspect

def get_type(cls, attr):
try:
return [a.kind for a in inspect.classify_class_attrs(cls) if a.name == attr][0]
except IndexError:
return None

class Test(object):
@classmethod
def foo(cls):
print 'foo'

def bar(self):
print 'bar'

@staticmethod
def baz():
print 'baz'

You can use it as:

>>> get_type(Test, 'foo')
'class method'
>>> get_type(Test, 'bar')
'method'
>>> get_type(Test, 'baz')
'static method'
>>> get_type(Test, 'nonexistant')
None

How does one use python's __import__() and getattr() to correctly to instantiate nested classes?

Two ways to do this, suppose you have a string representing attribute access and a nested object:

>>> from types import SimpleNamespace
>>> module = SimpleNamespace(foo=SimpleNamespace(bar=SimpleNamespace(baz='tada!')))
>>> module
namespace(foo=namespace(bar=namespace(baz='tada!')))

The first is to basically parse the string yourself by splitting and using getattr in a loop (or even, reduce!):

>>> from functools import reduce
>>> reduce(getattr, "foo.bar.baz".split('.'), module)
'tada!'

Which is just equivalent to:

>>> result = module
>>> for attr in "foo.bar.baz".split("."):
... result = getattr(result, attr)
...
>>> result
'tada!'

Or use the built-in functools.attrgetter factory function as a one-off:

>>> import operator
>>> operator.attrgetter("foo.bar.baz")(module)
'tada!'

Why use setattr() and getattr() built-ins?

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)

python getattr returns to handler when exactly is handler set to None?

Okay it seems that you use getattr() to map function calls by string name to a handler function which is a wrapper for the function you are trying to map over.

use one function to getattr for either functions or regular attributes

Solution 1

You can make length a property:

class Thing(object):
def __init__(self):
self.legs = 4
@property
def length(self):
return 6

>>> thing = Thing()
>>> thing.legs
4
>>> thing.length
6

If you really want to use your function:

def set_session_attribute(obj, attribute):
return getattr(obj, attribute)

>>> set_session_attribute(thing, 'legs')
4
>>> set_session_attribute(thing, 'length')
6

If you cannot change the source of thing directly, you can do after importing the class:

class Thing(object):
def __init__(self):
self.legs = 4
def length(self):
return 6

Here:

Thing.length2 = property(Thing.length)

>>> thing = Thing()
>>> thing.length2
6

Solution 2

Alternatively, you can check if the attribute is callable:

class Thing(object):
def __init__(self):
self.legs = 4
def length(self):
return 6

def set_session_attribute(obj, attribute):
attr = getattr(obj, attribute)
if hasattr(attr, '__call__'): # may use `callable()`
return attr()
return attr

>> thing = Thing()
>>> set_session_attribute(thing, 'legs')
4
>>> set_session_attribute(thing, 'length')
6

Use of getattr on a class with data descriptor

getattr(A, 'a') triggers the descriptor protocol, even on classes, so String.__get__(None, A) is called.

That returns None because your String.__get__() method has no explicit return statement.

From the Descriptor Howto:

For classes, the machinery is in type.__getattribute__() which transforms B.x into B.__dict__['x'].__get__(None, B).

getattr(A, 'a') is just a dynamic from of A.a here, so A.__dict__['x'].__get__(None, A) is executed, which is why you don't get the same thing as A.__dict__['x'].

If you expected it to return the descriptor object itself, you'll have to do so explicitly; instance will be set to None in that case:

class String(object):
def __get__(self, instance, owner):
if instance is None:
return self
def __set__(self, instance, value):
pass

This is what the property descriptor object does.

Note that the owner argument to descriptor.__get__ is optional; if not set you are supposed to use type(instance) instead.



Related Topics



Leave a reply



Submit