Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python

So what does setattr() and getattr() actually do?

In reference to the code in this thread:

https://teamtreehouse.com/community/my-address-book-class-with-search-function-regular-expressions-in-python-extra-credit

I'm wondering exactly is the purpose of this part of the code:

class Contact(object):
    def __init__(self, match):
        for key, value in match.groupdict().items():
            setattr(self, key, value)

    def __str__(self):
        return '\n'.join(
            sorted(
                ["\t{}: {}".format(
                                   key, val
                                   ) for key, val in self.__dict__.items()]))

In python documentation: it says that setattr(x, 'foobar', 123) is the same as x.foobar = 123.

So why when I substitute setattr(self, key, value) for self.key = value, the code behaves differently?

[MOD: fixed formatting -cf]

3 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,468 Points

As stated in the docs, setattr() is the counterpart of getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it. For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123.

setattr() can be used when the name of the attribute you wish to set is contained in a variable.. Say you have a set of keyword variables you wish to add to an object as attributes. If the name of the attribute you are trying to set is in held in a variable, then x.variable_name = "some value" won't work, but instead it will set the attribute name to literally "variable_name".

You can loop through kwargs and add them using setattr() without having to know the precise names of the keywords:

$ ipython3
Python 3.4.0 (default, Jun 19 2015, 14:20:21) 
Type "copyright", "credits" or "license" for more information.

IPython 1.2.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: class myobj():
   ...:         pass
   ...: 

In [2]: kwargs = {'username': "snakeherder", 'email': "sherder@example.com"}

In [3]: for key, value in kwargs.items():
   ...:     setattr(myobj, key, value)
   ...: 

In [4]: myobj.username
Out[4]: 'snakeherder'

In [5]: myobj.email
Out[5]: 'sherder@example.com'

From the docs: getattr(object, name[, default]) Returns 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.

The same case as above, when the attribute name is in a variable, trying to access x.variable_name won't work.

Using getattr(x, 'foobar') instead of x.foobar has the advantage of being able to specify a default. This prevents an error when looking up an attribute that may not exist. For example,

username = getattr(user, 'username', None)
if username:
    # do something

This is easier to read then having to wrap code in a try statement:

try:
    username = user.username
except AttributeError:
    username = None
else:
    # do something

Digging specifically into your example code:

class Contact(object):
    def __init__(self, match):
        '''Initialize an instance using contents of 'match'

        match is an 're.match()' object
        match.groupdict() is a dict of successful matches
        '''
        # unpack each dict itemin to key:value pair 
        for key, value in match.groupdict().items():
            # set the attribute 'key' to 'value
            setattr(self, key, value)

    def __str__(self):
        '''Instance string method. 

        Returns a string of attributes in the form "key: value".
        The string is constructed by generating a list of strings
        where each string is in the form "key: value". The 'key', 
        'value'attributes are generated by unpacking the output
        the '__dict__.items()' method which lists all attributes
        in a (key, value) tuple. This list of strings is then sorted
        then joined together using a newline character to form
        a multi-line string.
        '''
        return '\n'.join(
            sorted(
                ["\t{}: {}".format(
                                   key, val
                                   ) for key, val in self.__dict__.items()]))

This doesn't wholly address my question in regards to that specific example, but it is enough for me to kind of put the pieces together and understand it a bit better now. Thank you. Now, in addition in regards to piece of code I past above:

self.__dict__.items

Does this essentially refer to the object's attributes? If so that would make sense why setattr() was used in conjunction with the dict magic method here. If you can add any more info regarding, this that would be extremely helpful

Chris Freeman
Chris Freeman
Treehouse Moderator 68,468 Points

Answer updated to include your code question.

self.__dict__ refers to an object's namespace, and all the references within it, in a dict format. Which is to say: all it's attributes.

.items() is the regular dict method to return tuples of (key, value) pairs for all of the items in a dictionary.

Combined together, self.__dict__.items() returns all of an objects attributes in (key, value) pairs.

I'd be happy to elaborate further if needed.

I'd like to suggest adding a key bit of explanation to this course: how to display/see the values that are collected using setattr. It's not obvious at all in the Python documentation, and is easy to misunderstand. This could be as simple as "To see the values that were set via **kwargs and setattr(), use ...." and provide an example. For a beginner, setattr does some opaque magic that it's not obvious how to see inside of.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,468 Points

AFAIK there isn't a mechanical to tell how an attribute was set. Using setattr() vs self.variable = value or whether it was done during instantiation within the __init__ method vs post-initialization through direct attribute assignment.

To see all the all object attributes use dir(object)

To see the attributes and their values use object.__dict__

Was there something else you had in mind?

Chris Freeman ,Don't you feel our python teacher ,which Mr Love, doesn't really explain it in more detail. He just explain some "key part" within few second ,such as setattr. It is just like he writes his code and doesn't really explain everything.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,468 Points

In support of Kenneth Love, choosing how much detail to provide is a delicate balance. Good books on Python span over 1600 pages. Many times Kenneth has to settle for a basic introduction of a concept or a simple mention. The Community forum and Teacher's notes beneath the videos are a great way to dig deeper into topics. Often searching the existing posts may provide the direction needed to go as far as you wish into details.