87

I am looking for someone to explain the basics of how to use, and not use setattr().

My problem arose trying to use one class method/function to return data that is then put in another method/function. Perhaps a simpler approach would be much better in this case, but I'm trying to understand how classes work/are used. This problem seems to hinge on setattr(), and this is my attempt to make a fairly simple use of this.

Though it's not quite the same problem, I was following Python The Hard Way, ex42—the while loop @ lines 18-41.

I tried writing an \__init__(), and using getattr() instead, thinking perhaps something needed to be in the class' namespace, but this doesn't seem to help.

#! /bin/python2.6

class HolyGrail(object):

    def __init__(self):
        self.start = 'start_at_init'

    # function definition in question:
    # TypeError: 'str' object is not callable

    def run_it(self):
        start = setattr(self, 'name', 'get_thing')
        start = self.name

        # Something wrong here?
        value_returned = start() #I believe this == self.get_thing()
        use_it(value_returned)

    """
    # alternate function definitions
    # NameError: global name 'start' is not defined

    def __init__(self):
        self.start = 'get_thing'

    def run_it(self):
        go_do_it = getattr(self, start)
        first_output = go_do_it()
        use_it(first_output)
    """

    def get_thing(self):
        return "The Knights Who Say ... Ni!"

    def use_it(self, x):
        print x
        print "We want a shrubbery!"

my_instance = HolyGrail()
my_instance.run_it()

@Karl Knechtel, @Amber , @Chris Morgan thanks for your help.

I think I can now explain my own answer! This required a better grasp of self as an object for me. It's an instance name that gets tagged up with stuff like attributes.

The class could be a Town, and then. getattr looks for a house using it's name so you are ready to call on it soon, and comes up with a different place if you don't find the house --With getattr a 'name' exists, and you go find it. Makes the step from one function to another dynamic As a bonus you may have a default value, useful to get a fallback default method--connection failed or something?

setattr builds a house and gives it a name so you can call in on it later. You could potentially rebuild this house, or go to a particular place if you are unable to find it. --setattr makes an attribute name and gives, or changes it's value, to be called on later Perhaps a user turns sound off, then future methods don't output any audio.

I could have written my function a number of ways, but there's no need to change any attributes:

def run_it(self):
    yo = getattr(self, 'get_thing')
    answer = yo()
    setattr(self, 'deal_accepted', self.use_it) #really ott
    no = getattr(self, 'deal_accepted')
    no(answer)

Properly corrected code:

def run_it(self):
    value_returned = self.get_thing()
    self.use_it(value_returned)
bounce
  • 981
  • 1
  • 6
  • 5
  • 9
    `setattr` is for situations where you can't do it directly. As a beginner, these should be nonexistent. `setattr(self, 'name', 'get_thing')` means exactly the same as `self.name = 'get_thing'`. – Karl Knechtel Mar 05 '12 at 06:58
  • please don't use docstring notation for a block comment. use # instead. http://www.python.org/dev/peps/pep-0008/ – monkut Mar 07 '12 at 05:20
  • 5
    @monkut: triple-quoted string, not docstring; "docstring" just refers to a string which is the first statement in a module, class or function definition (or the `__doc__` attribute of an object). – Chris Morgan Mar 07 '12 at 05:46
  • 2
    you're right. In any case don't use strings as comments. – monkut Mar 07 '12 at 05:53
  • 1
    @monkut, why shouldn't one use strings as comments? – Haleemur Ali Jun 15 '15 at 17:22
  • 5
    @Haleemur Ali Generally speaking comments are intended to provide clarification of code actions, and are not intended as output of the program. A commented line is not analyzed by the interpreter, so it does not affect run time behavior. Creating strings consumes memory and processing time. – monkut Jun 16 '15 at 00:48
  • @HaleemurAli And they are not colored the same by your IDE (hopefully), so filling the code with unassigned strings instead of real comments is visual pollution. – Guimoute Jul 09 '21 at 13:58

6 Answers6

106

The Python docs say all that needs to be said, as far as I can see.

setattr(object, name, value)

This 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.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • 13
    Thanks. Question, if dot notation is so much more succinct, then why use setattr at all? – Nostalg.io Nov 05 '14 at 03:48
  • 37
    @halfnibble: it’s useful when `name` is not a string literal, when the attribute you are setting is dynamically determined, e.g. in some forms of metaprogramming. – Chris Morgan Nov 05 '14 at 20:53
  • 7
    @halfnibble: An example: `for key in data: value = request.DATA.get(key) setattr(obj, key, value)` <-- iterates through all provided POST data and assigns to obj using key as the field. – e.thompsy May 18 '15 at 20:16
  • 3
    @Chris Morgan, how do some objects disallow `setattr` ? E.g. `x = numpy.ones(3); setattr( x, "name", "xname" )` --> AttributeError ? (I'll ask a new question if you like) – denis May 29 '16 at 10:38
  • 3
    @denis: `setattr` doesn’t necessarily permit you to set an attribute of any name on an object. If the type uses `__dict__`, it does, but a type produced in C with a known set of attributes may not, or from Python code a type which defines `__slots__`. In such cases, you can use `setattr` to set acceptable attributes, but others will produce an `AttributeError`. – Chris Morgan May 29 '16 at 22:22
  • Thanks. I just wanted numpy arrays with names, but seems you'd have to subclass ndarray, don't try this at home – denis May 30 '16 at 13:12
  • Is there a python architectural reason the method doesn't return a boolean of success? – Eduardo Pignatelli Sep 07 '18 at 11:47
  • 1
    @EduardoPignatelli: returning a bool is for cases where True and False are both normal outcomes. This is not the case when calling setattr(); there, failure is decidedly abnormal, strongly indicating a programming error, and raising an exception in case of failure and returning nothing in other cases is the appropriate course of action in Python. This also makes it so that setattr() is precisely equivalent to the corresponding assignment statement, which produces nothing and can raise an AttributeError. – Chris Morgan Sep 13 '18 at 09:32
85

You are setting self.name to the string "get_thing", not the function get_thing.

If you want self.name to be a function, then you should set it to one:

setattr(self, 'name', self.get_thing)

However, that's completely unnecessary for your other code, because you could just call it directly:

value_returned = self.get_thing()
Amber
  • 507,862
  • 82
  • 626
  • 550
  • I see! Got it! You just have to tell python where the function is, self! (so getattr's _value_ would also also be self.function). Thank you Amber! – bounce Mar 05 '12 at 04:10
  • Is there a way to access the attribute's name `'name'` inside the `get_thing()` function ? – zml Aug 11 '17 at 14:09
  • AFAIK not unless you, again, `setattr` it on the container function. – bjd2385 Aug 15 '19 at 17:19
14

Setattr: We use setattr to add an attribute to our class instance. We pass the class instance, the attribute name, and the value. and with getattr we retrive these values

For example

Employee = type("Employee", (object,), dict())

employee = Employee()

# Set salary to 1000
setattr(employee,"salary", 1000 )

# Get the Salary
value = getattr(employee, "salary")

print(value)
kuldeep Mishra
  • 228
  • 2
  • 4
7

To add to the other answers, a common use case I have found for setattr() is when using configs. It is common to parse configs from a file (.ini file or whatever) into a dictionary. So you end up with something like:

configs = {'memory': 2.5, 'colour': 'red', 'charge': 0, ... }

If you want to then assign these configs to a class to be stored and passed around, you could do simple assignment:

MyClass.memory = configs['memory']
MyClass.colour = configs['colour']
MyClass.charge = configs['charge']
...

However, it is much easier and less verbose to loop over the configs, and setattr() like so:

for name, val in configs.items():
    setattr(MyClass, name, val)

As long as your dictionary keys have the proper names, this works very well and is nice and tidy.

*Note, the dict keys need to be strings as they will be the class object names.

QuantumChris
  • 963
  • 10
  • 21
5

Suppose you want to give attributes to an instance which was previously not written in code. The setattr() does just that. It takes the instance of the class self and key and value to set.

class Example:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
AbstProcDo
  • 19,953
  • 19
  • 81
  • 138
1

The first thing that strikes me about your code is that you set start to the value of setattr.

start = setattr(self, 'name', 'get_thing')
start = self.name

# Something wrong here?
value_returned = start() #I believe this == self.get_thing()
use_it(value_returned)

The return value of setattr is None. It is an operation that is performed and does a task, but does not return a value. Similar to calling something like exit(), the operation just runs. Enter python from your shell and you'll see

a = exit() #exits your program

Setting a to print('x') performs like exit and like your setattr, however, calling upon a doesn't invoke our method.

a = print('15') #returns 15
a #returns None

if you want to be able to call upon setattr from within your class, you could define a method for it.

def setAtrrClassInstance(self,name,value):  #This returns None
    setattr(self,name,value)
Neuron
  • 5,141
  • 5
  • 38
  • 59
Brakke Baviaan
  • 460
  • 3
  • 10