1

So I have a class with the following method:

def Add(self,new_name):
        self.new_name=.... 

I want to add an attribute with the title "new_name" that will be passed from somewhere else. Obviously, in the above program I simply created an attribute with the string "new_name" instead of the actual argument input.

How should I go about making sure the attribute's name is the actual parameter? I believe in C++ it's easy to use c_str() here, but with Python I'm not sure.

ex. Add('hello') should create a new attribute self.hello which I can then store with information. Thank you.

EDIT: Ok I see I should use setattr() now. However, I seem to not be getting the expected results. Is this how I would use it where the initialization is a list comprehension? I tried the two ways and neither worked as shown below:

def Add(self,new_name):  #also doesn't work

        setattr(self, new_name,[API(self.var1,str(new_name),i) for i
        in range(self._NumRows)])  

def Add(self,new_name):  #also doesn't work
        setattr(self, new_name,[]) 
        self.new_name=[API(self.var1,str(new_name),i) for i
        in range(self._NumRows)])  

where API takes 3 arguments (I know this isn't the problem because this works fine as long as I don't use setattr() and the name is already known so it's nothing with the API.

EDIT: For those suggesting dictionary, I'm not storing data with these attributes. I'm creating a method which creates a new attribute for users who are creating derived classes of my base class. It's to abstract away the creation of additional attributes because I was told my code would likely be adopted in the future when I leave (I'm an intern) and to make it as easy as possible to create derived classes.

EDIT: Nevermind, I mislabeled a variable. It works! Thank you.

masque
  • 177
  • 7
  • 1
    "I believe in C++ it's easy to use c_str() here, but with Python I'm not sure." - `c_str()` would do nothing helpful. It is not actually possible to add new dynamically-named members to C++ objects. – user2357112 Jul 20 '18 at 21:01
  • Does it have to be an attribute or you just need a way to map the "new_name" strings to some values for each of objects? – gaganso Jul 20 '18 at 21:07
  • You're right. I'm thinking of using it with an array or vector. Is there a way to do this in Python? – masque Jul 20 '18 at 21:08
  • @gaganso It has to be an attribute. – masque Jul 20 '18 at 21:09
  • @AndrejKesely Why the format string? – MegaIng Jul 20 '18 at 21:12
  • 1
    @AndrejKesely But why the format string? Just `new_name` or `str(new_name)` should be enough. – MegaIng Jul 20 '18 at 21:14
  • @MegaIng You have right, the format string is redundant...I misunderstood op's question :) – Andrej Kesely Jul 20 '18 at 21:17
  • 2
    How would you use it once you'd created it? Are you planning on generating code? – Peter Wood Jul 20 '18 at 21:21
  • Possible duplicate of [Dynamic variable name in python](https://stackoverflow.com/questions/2564140/dynamic-variable-name-in-python) – Peter Wood Jul 20 '18 at 21:23
  • Are you **really** sure you want to do this? Making variable / attribute names depend on run-time data is generally a bad idea. It makes all the code that has to deal with those names more verbose, more complicated, and harder to debug, than it needs to be. Dynamic attribute names aren't as bad as dynamic variable names, but it's still a code smell. Keep data out of your variable names! See [Why you don't want to dynamically create variables](http://stupidpythonideas.blogspot.com/2013/05/why-you-dont-want-to-dynamically-create.html). – PM 2Ring Jul 20 '18 at 21:28
  • @PM2Ring Dynamic attribute names are exactly as bad as dynamic variable names. The only difference, really, is whether the namespace you're junking is a module namespace or an instance/class namespace. – wim Jul 20 '18 at 21:40
  • Why doesn't the first version work? What is the problem? – MegaIng Jul 20 '18 at 22:12

2 Answers2

2

If you know what you are doing (and a normal dictionary is not an option), you can use setattr:

def Add(self, new_name, value):
    setattr(self, new_name, value)

This should be one of your last resorts.

Also if the using code knows new_name at write time (so it is not create by the program), instead of this :

self.Add('hello', 'test')

You could just write

self.hello = 'test'

This depends on how you are using this function.

MegaIng
  • 7,361
  • 1
  • 22
  • 35
  • Normal dictionary is not an option because I'm not storing data really. I'm creating a method which creates a new attribute for users who are creating derived classes of my base class. It's to abstract away the creation of additional attributes. I'm about to update post – masque Jul 20 '18 at 21:39
2

This is exactly setattr:

def add(self, attr_name):
    val = ...
    setattr(self, attr_name, val)

The caveat is that you need to use getattr to retrieve val (and delattr to delete it), and any code that gets/deletes also needs to know the attr_name in order to do that.

Make your life easier and use a dict for data, not attributes.

wim
  • 338,267
  • 99
  • 616
  • 750
  • I'm not storing data really. I'm creating a method which creates a new attribute for users who are creating derived classes of my base class. It's to abstract away the creation of additional attributes. I'm about to update post. – masque Jul 20 '18 at 21:26
  • @masque I see. I hope your update clearly explains why the derived classes can't create those new attributes in the usual way. ;) – PM 2Ring Jul 20 '18 at 21:31
  • @PM2Ring They can, but I'll be leaving this project I'm working on starting soon (I'm an intern), and I was told to make it as easy as possible to create derived classes from this base class because it's going to be adopted I'm told. One of the inherent differences in these derived classes is additional attributes so I want to make a method to create a new attribute which can then be used with an existing API. It would take the programmer 20 lines for each derived class to redo what I did in base class and only 1 if I did this. – masque Jul 20 '18 at 21:38