9

Trying to access/assign items in a list with getattr and setattr funcions in Python. Unfortunately there seems to be no way of passing the place in the list index along with the list name.
Here's some of my tries with some example code:

class Lists (object):
  def __init__(self):
    self.thelist = [0,0,0]

Ls = Lists()

# trying this only gives 't' as the second argument.  Python error results.
# Interesting that you can slice a string to in the getattr/setattr functions
# Here one could access 'thelist' with with [0:7]
print getattr(Ls, 'thelist'[0])


# tried these two as well to no avail.  
# No error message ensues but the list isn't altered. 
# Instead a new variable is created Ls.'' - printed them out to show they now exist.
setattr(Lists, 'thelist[0]', 3)
setattr(Lists, 'thelist\[0\]', 3)
print Ls.thelist
print getattr(Ls, 'thelist[0]')
print getattr(Ls, 'thelist\[0\]')

Also note in the second argument of the attr functions you can't concatenate a string and an integer in this function.

Cheers

insomniaccanuck
  • 91
  • 1
  • 1
  • 2
  • 1
    @BrainStorm's response is correct, but this generally smells of "your going about it the wrong way". What are you trying to accomplish in the end? – lambacck Aug 01 '11 at 03:17
  • What in the world...? Items in the list aren't attributes of the list, so this obviously won't work. – kindall Aug 01 '11 at 03:18
  • What are you **really** trying to do? – Karl Knechtel Aug 01 '11 at 04:27
  • @Karl What I am **really** trying to do? I'm trying to use string data that is the name of a list in a class to assign or call one item of that list. Seems like getattr/setattr don't have the functionality I am looking for. – insomniaccanuck Aug 01 '11 at 04:59
  • 1
    @insomniaccanuck we know what you are attempting to do in this instance, but there is a larger problem you are trying to solve that has driven you to try these this. Perhaps you are [trying to parse a hierarchical query string into nested objects/lists](http://stackoverflow.com/questions/6687698/python-parsing-query-string-to-list/6688161) or maybe you want a config item that lets you uniquely traverse a nested object hierarchy from a config item or maybe you are trying to do something else, but more information about the larger picture will help us guide you to a solution. – lambacck Aug 01 '11 at 13:49
  • 1
    OK, more reading and pondering I think what is best is to use a dict with lists nested inside. The function setattr doesn't have the funcionality I need (probably with good reason). What I was trying to do was build an n-dimensional set to act on with a permutation group.I come from a combinatorics background; don't have that much CS lingo yet. Learning python (and coding) to try and find a job. Thanks to all – insomniaccanuck Aug 01 '11 at 23:29
  • @insomniaccanuck you might want to look at `collections.defaultdict` since you can use `map_to_list = defaultdict(list)` and then when you are adding to the lists use `map_to_list[key].append(value)` and always be sure there is a list. There is also `collections.Counter` which acts kind of like a multi-set. – lambacck Aug 03 '11 at 01:10

3 Answers3

9
getattr(Ls, 'thelist')[0] = 2
getattr(Ls, 'thelist').append(3)
print getattr(Ls, 'thelist')[0]

If you want to be able to do something like getattr(Ls, 'thelist[0]'), you have to override __getattr__ or use built-in eval function.

warvariuc
  • 57,116
  • 41
  • 173
  • 227
  • Well that's half of it. Thanks. Thought I had tried this for both setattr and getattr but I guess I only tried for setattr. It produces this error for setattr: setattr(Ls, 'thelist', 3)[2] TypeError: 'NoneType' object is not subscriptable – insomniaccanuck Aug 01 '11 at 05:04
  • `getattr(Ls, 'thelist')` returns `thelist`attribute, which is a list. Do with the returned result whatever you want. `'NoneType' object is not subscriptable` this means the when you call this `thelist` is None, when it should be `[]` (list). Initialize it first. – warvariuc Aug 01 '11 at 05:11
5

You could do:

l = getattr(Ls, 'thelist')
l[0] = 2  # for example
l.append("bar")
l is getattr(Ls, 'thelist')  # True
# so, no need to setattr, Ls.thelist is l and will thus be changed by ops on l

getattr(Ls, 'thelist') gives you a reference to the same list that can be accessed with Ls.thelist.

Skylar Saveland
  • 11,116
  • 9
  • 75
  • 91
BrainStorm
  • 2,036
  • 1
  • 16
  • 23
  • Thanks for this feasible work-around but it doesn't scale nicely. That is you have to assign the whole list over again. Trying to just assign/call one item from the list. – insomniaccanuck Aug 01 '11 at 04:04
  • 1
    There is no need to do `setattr(Ls, 'thelist',l)`. The Ls.thelist and and l are the *same* list and so setting `l[0]` in this case is equivalent to `Ls.thelist[0]=2` – lambacck Aug 01 '11 at 15:19
  • @lambacck, i don't think so... but i didn't test so i might be wrong. – BrainStorm Aug 01 '11 at 16:22
  • 2
    In end result it is the same but BrainStorm's method allows you to assign via string data. Your's is a direct assign by name which doesn't solve my problem. – insomniaccanuck Aug 01 '11 at 17:16
  • 1
    @BrainStorm: Please see [this article about "Call by Object](http://effbot.org/zone/call-by-object.htm) – lambacck Aug 02 '11 at 01:52
  • 1
    `Ls.thelist is getattr(Ls, 'thelist')` so, yeah, `setattr` line is not needed. I think I will edit to remove. – Skylar Saveland Sep 13 '12 at 00:51
3

As you discovered, __getattr__ doesn't work this way. If you really want to use list indexing, use __getitem__ and __setitem__, and forget about getattr() and setattr(). Something like this:

class Lists (object):

    def __init__(self):
        self.thelist = [0,0,0]

    def __getitem__(self, index):
        return self.thelist[index]

    def __setitem__(self, index, value):
        self.thelist[index] = value

    def __repr__(self):
        return repr(self.thelist)

Ls = Lists()
print Ls
print Ls[1]
Ls[2] = 9
print Ls
print Ls[2]
Ethan Furman
  • 63,992
  • 20
  • 159
  • 237