1

I'm trying to do the following:

class DoSomething:
   def something(self, number, name)
      self.main_dict = {}
      setattr(self,name,['anything'])
      self.main_dict[number] = getattr(self,name) 

The above passes the attribute (reference by value) to the dictionary. Not the behavior I need.

I want to pass the reference of the dynamic variable to the dictionary, not the attribute. Further on I modify the dynamic variable and I want it to be reflected in the dictionary.

Is it possible do so such thing?


Edit:

Why?

1) To learn how to do such thing and have one more tool in the toolbox. The answers provided refer to using a "reference / container" class and creating an instance for each value needed. Perfect.

2) Because this problem was created by another fixable problem (didn't see at first). Note I further modify the dynamically created variable. To do so, it needs to be dynamically called by (setattr, vars, __dict__, etc). It was not being modified, it was being assigned a new value which changes the reference, not reflecting back to the dict.

I would like to point to this about assignment/reference: How do I pass a variable by reference?

3) The intent is maintenance. By referencing at the beginning the dict-variable, the programmer would know any changes made to the variable would reflect into the dictionary.

4) I mentioned in the comments about using lambda. This would only work if the name is never modified, otherwise lambda would use the last assigned value to name.

Community
  • 1
  • 1
NovoRei
  • 261
  • 1
  • 3
  • 7
  • One "solution" to make it work is to use lambda: self.main_dict[number] = lambda : **getattr**(self,name) And to call it another place: self.main_dict[numero]()[0] – NovoRei Nov 18 '14 at 23:36
  • 1
    Why are you using `getattr` and `setattr` instead of just assigning to and referencing class variables? – furkle Nov 18 '14 at 23:36
  • 1
    Because OP is trying to programmatically assign properties onto the object. –  Nov 18 '14 at 23:44
  • @Lattyware That won't work. `self.name = "thing"` is different to `name = "foo"; setattr(self,name,['thing'])`. The former sets `self.name`, the latter sets `self.foo`. –  Nov 18 '14 at 23:45
  • Its possible, but can you explain a little more about what you are trying to do? –  Nov 18 '14 at 23:46
  • @LegoStormtroopr Right, I see that, but *why*? a. This seems like a very roundabout way of doing anything and b. I'm not really sure what the something he's trying to accomplish is. – furkle Nov 18 '14 at 23:47
  • @furkle "Why?" is a very good question, but it *can* be done. –  Nov 18 '14 at 23:49
  • 1
    @LegoStormtroopr Wow, can't believe I didn't see that, indeed. Just saw what I wanted to see there, apparently. Although generally dynamically setting properties is a bad idea in itself (use a data structure like a `dict` instead). – Gareth Latty Nov 18 '14 at 23:49
  • @furkle Other methods use the string/int contained in name/number to access the dictionary. They come from an external file which is being parsed. – NovoRei Nov 18 '14 at 23:51
  • Yeah its a subtly in the code that is seldom seen, and can confuse people. Which is why the OP needs a really good reason to be dynamically adding properties to an object. –  Nov 18 '14 at 23:55

3 Answers3

0

I think you are looking for something like this:

class DoSomething(object):
   def something(self, number, name):
      self.main_dict = {}
      setattr(self, name, ['anything'])
      setattr(self, number, getattr(self, name))

setattr can set an attribute whose name is given by the value of name. getattr will return the value of the attribute whose name is given by the value of number.

As an example of the above in use:

In [33]: s = DoSomething()

In [34]: s.something('x', 'alpha')

In [35]: s.x
Out[35]: ['anything']

In [36]: s.alpha
Out[36]: ['anything']
John1024
  • 109,961
  • 14
  • 137
  • 171
0

Yes. This will work.

But, dynamically setting properties on classes isn't a good idea because it become difficult to keep track of what is available and what is not depending on when an object is instantiated, or accessed.

class Thing(object):
   def setP(self,name,value):
       setattr(self,name,value)
   def getP(self,name):
       return getattr(self,name)

foo = Thing()
foo.setP('bar','baz')
print foo.bar

Gives:

> baz

However, one of the advantages of a class is that (ideally) it puts forth a public contract regarding public members and functions. If you can't be reasonably certain that an object will have a property at any given time, or that two instantiations of the same class won't have the same properties - (all credit to furkle for this)

In the above code this will not work:

foo = Thing()
print foo.bar

And will raise an exception, so the removal of one line outside of the class has made a call to access an object property fail.

Likewise:

foo = Thing()
goh = Thing()
foo.setP('bar','baz')
print foo.bar
print goh.bar

This will also throw an exception. Dynamically assigning attributes to obejcts makes it difficult to known when certain objects are available.

Community
  • 1
  • 1
  • 1
    why is dynamcally setting properties on classes bad? you can also dynamically check for them later. – tdelaney Nov 19 '14 at 00:00
  • @tdelaney I've added some examples of why this is bad. –  Nov 19 '14 at 00:04
  • 1
    @tdelaney Because one of the advantages of a class is that (ideally) it puts forth a public contract regarding public members and functions. If you can't be reasonably certain that an object will have a property at any given time, or that two instantiations of the same class won't have the same properties, doesn't that defeat a lot of the purpose of creating your own object in the first place? – furkle Nov 19 '14 at 00:08
  • @furkle That was brilliantly put, I've edited your comment into the answer and made it a community wiki as your wording makes the answer infinitely better and I couldn't accept rep on your behalf. –  Nov 19 '14 at 00:12
  • @LegoStormtroopr - its common in metaprogramming and duck typing. If you ever have a method that adds a variable (e.g., `self.conn = socket.accept()`) without a `self.conn = None` in `__init__`, well, you just did it yourself. I add information to class objects all the time, most programmers do. – tdelaney Nov 19 '14 at 00:17
  • Note: I didn't say its bad, just that its not good and can be "become difficult to keep track of". –  Nov 19 '14 at 00:22
  • @LegoStormtroopr - you claim its not a good idea. I disagree. – tdelaney Nov 19 '14 at 00:27
  • 1
    @tdelaney Right, and it's partially a bias of mine that I like to reasonably assume that any `Foo` object is going to have a `conn` property, for example. But mostly my point, and I think Lego's, is that in this case there doesn't seem to be any gain whatsoever for the OP in using metaprogramming. – furkle Nov 19 '14 at 00:28
  • @furkle - OP is juggling 3 things: a name, a number and a value that changes. He uses the object dict to hold the name and main_dict to hold the number. Classes are containers that hold "related data" - its a dynamic language and sometimes it makes sense to add it dynamically. – tdelaney Nov 19 '14 at 00:42
  • And this answer doesn't exactly work because it doesn't maintain the numerical reference to the data. – tdelaney Nov 19 '14 at 00:43
  • @tdelaney In that case, I think you understand the OPs problem better than me. I'm still confused - why reinitialize the dictionary with every call of `something`? I'm perhaps overly conservative about metaprogramming, but in this case, what's the point in metaprogramming if the dictionary only ever holds a single key-value pair? (Note also that I didn't downvote you - your solution looks perfectly fine.) – furkle Nov 19 '14 at 00:49
-1

You can do this by defining a mutable object that holds the values you want. The class dict and main_dict point to this object and it holds a value you can change dynamically.

class DynValue(object):

    __slots__ = 'val'

    def __init__(self, val):
        self.val = val

    def __str__(self):
        return str(self.val)

    def __repr__(self):
        return 'DynValue{%s}' % repo(self.val)



class DoSomething:
    def something(self, number, name):
        self.main_dict = {}
        val = DynValue(['anything'])
        setattr(self,name,val)
        self.main_dict[number] = val

    def sometimelater(self, name):
        getattr(self, name).val = ['somethingelse']


d=DoSomething()
d.something(1, 'fred')
print d.main_dict[1]
d.sometimelater('fred')
print d.main_dict[1]

Its also common to have one container hold the value and have the other containers refer back to it. So, if you store the value directly on self, you'd store the name in main_dict and lookup every time.

class DoSomething:
   def something(self, number, name)
      self.main_dict = {}
      setattr(self,name,['anything'])
      self.main_dict[number] = name

   def get_by_number(self, number):
      return getattr(self, self.main_dict[number])

   def set_by_number(self, number, newval):
      setattr(self, self.main_dict[number], newval)

   def set_by_name(self, name newval):
      setattr(self, name, newval)
tdelaney
  • 73,364
  • 6
  • 83
  • 116