I want badges
I want badges

Reputation: 6515

Method that applies to instance of a class in python // Apply a method to an instance of a class in python

I'm starting to create classes in python and I am very confused about the terms, so I can't find the answers to my questions since I don't know how to ask those,hence the double title of this post. I will try to clarify the question I don't know how to ask with the following code:

I want to create a method within a simple class:

class SimpleClass:
    add(self,Value)
        #Magic goes here

SC=SimpleClass()

The method should be equivalent to adding a new variable to the class:

SC.NewVariableInsideTheClass="test"

But through a method, for instance:

SC.NewVariableInsideTheClass.add("test")

In both cases "NewVariableInsideTheClass" did not exist before, I am creating that variable for the first time inside the class.

So the method should first create the non existing instance (i think that is the name for a variable inside a class) and then set its value to "test", but I really can't find/search for how to do that...

Which would be the right question for what I need??

Thank you very much!

----------------- EDIT ---------------------

Ok, so what I want to do is to get the same results when doing:

SC.NewVariableInsideTheClass.add("test")

and when doing:

SC.NewVariableInsideTheClass.add("test")

But both must be done outside the class, with "NewVariableInsideTheClass" being the first time used! The objective here is to use the class as a variable storage, such as when a variable is used for the first time something will happen, and if it already exists (in the form of SC.NewVariableInsideTheClass) I will be able to use it differently. Hope this is more specific now...

SECOND EDIT; REASON WHY -------------------------

Wow im impressed by the quality and detail of your answer, I'm very grateful for that! Id like you to know the reason why I want this: I am creating an easy python scripting IDE (right now is mostly for learning python, nhot useful yet). So the basics of the code is that it creates new variables along the way. Since the program will also create functions, my first solution for having variables to be created and handled was to make all the variables in the created program globals, and call them again in every new function. Well that code was ugly so I decided to learn and use a class, But since the variables differ in every program, and I want to standarize the function creation(Ie not needing to know if its the first function, if it needs to initialize any variable, or if they already exist), the way to be able to: -Store a new variable in a list named as the variable even if it doesnt exist, with a name which I can easily access to -Be able to extend that previous variable with new values as new items in the list -Be able to retrieve those values All without using globals, I came across this solution. Actually I modified your code, now I'm ussing your code, but instead of naming "add" I named "append", so I can freely do:

SC.NewVar.append('this is new value') SC.NewVar.append('this is new value')

And the first time, it will be created (since it doesnt exist) but the second time the new value is appended, hence, I will be able to call it/play with it or use it from any other function in the automatically-generated-code :)

Upvotes: 1

Views: 182

Answers (2)

Bakuriu
Bakuriu

Reputation: 101969

Disclaimer: I have no idea why you want to do this. If you want to dynamically create an attribute using setattr ought to be enough:

SC = SimpleClass()
setattr(SC, 'your_new_attribute_name', 'test')

Anyway, there is a simple way of obtaining what you want using an helper class:

In [1]: class SimpleClass:
   ...:     def __getattr__(self, attr):
   ...:         return AttributeSetter(self, attr)
   ...: class AttributeSetter:
   ...:     def __init__(self, inst, attr):
   ...:         self._inst = inst
   ...:         self._attr = attr
   ...:     def add(self, value):
   ...:         setattr(self._inst, self._attr, value)
   ...:         

In [2]: SC = SimpleClass()

In [3]: SC.new_attr.add('test')

In [4]: SC.new_attr
Out[4]: 'test'

Regarding your comment. If you are asking whether it is possible to write SimpleClass such that:

SC.new_attr

return the string 'test' and, after calling Sc.new_attr.add('test') the call:

SC.new_attr.add('other')

Is such that:

SC.new_attr

now returns the string 'other', then the answer is: No, it's impossible.

It's impossible to write a class such that the following does not produce an AttributeError:

In [2]: SC = SimpleClass()

In [3]: SC.new_attr.add('test')
In [4]: SC.new_attr   #this returns a string, not a user-defined class
'test'

In [5]: SC.new_attr.add('new value')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-f218074b7a68> in <module>()
----> 1 SC.new_attr.add('new value')

AttributeError: 'str' object has no attribute 'add'

Why do you ask? Well, let's look at what the interpreter actually does:

In [5]: import dis
   ...: dis.dis(lambda: SC.new_attr.add('some string'))
   ...: 
  2           0 LOAD_GLOBAL              0 (SC) 
              3 LOAD_ATTR                1 (new_attr) 
              6 LOAD_ATTR                2 (add) 
              9 LOAD_CONST               1 ('some string') 
             12 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             15 RETURN_VALUE 

The dis module allows us to look at the bytecode that the interpreter is executing. Note how there are two, distinct LOAD_ATTR bytecodes. As you can see the interpreter first gets the new_attr attribute and afterwards, as a completely independent operation, it retrieves the add attribute.

Now, the class SC can only modify the behaviour of the first attribute access(i.e. the access to new_attr). It has no way of knowing that the next bytecode will access add, hence the only sensible thing to do is to return the string that was set.

The following attribute access, the add, is done on the value retrieved. This means that the interpreter looks for the add attribute of the string, which obviously doesn't exist.

The only way to avoid this AttributeError would be to return an object that has an add method, but this means that doing:

In [6]: SC.new_attr
Out[6]: 'test'

Would fail because instead of returning the string "test" we'd return a different object.

A way around this would be to return an object that has an add method and also a value attribute, so that we'd obtain something like:

In [12]: SC.new_attr
Out[12]: <__main__.TheClass at 0x7fac44727a90>

In [13]: SC.new_attr.value
Out[13]: 'test'

In [14]: SC.new_attr.add('other')

In [15]: SC.new_attr.value
Out[15]: 'other'

A different solution would be to write this class so that it behaves like a string(we could even subclass str but don't do it! subclassing built-in is never the right thing to do, and if you do it without knowing the exact consequences you will have problems and they will be hard to spot and solve), but this means that the code would become a lot more complex, having to implement all the operations that string support(which means implementing about 80-90-100 methods!)

Upvotes: 4

Supreet Sethi
Supreet Sethi

Reputation: 1806

Lets declare a class ABC with a method add

class ABC(object):
    def add(self, var):
         self.new_var = var

Now add is instance method and thus accomplishes what you have set out to do which is to declare an instance variable within a instance method.

 a = ABC()
 # Make an instance
 a.add('test')
 # created a new variable inside instance
 a.new_var
 'test'

Upvotes: 0

Related Questions