Reputation: 35
I am trying to use a method defined in a class to later modify an attribute of an object made from that class, but there appears to be a scope problem that is not obvious to me. Take the following code:
class Test:
def __init__(self, attribute):
self.attribute = attribute
def change_attrib(self, attribute, value):
if attribute + value < 0: attribute = 0
else:
attribute += value
print(str(attribute) + ' This is inside the method')
test1 = Test(10)
print(str(test1.attribute) + " This is before running the method")
test1.change_attrib(test1.attribute, 10)
print(str(test1.attribute) + " This is after running the method")
test1.attribute += 20
print(str(test1.attribute) + " This is after modifying the attribute directly")
Running this code produces the following result:
10 This is before running the method
20 This is inside the method
10 This is after running the method
30 This is after modifying the attribute directly
So it appears that even though I am explicitly referring to the instance that I want to change when calling the method, everything that happens in the method, stays in the method.
I can see that modifying the attribute directly works, but I would like a guard against negative values (hence the method). I also know I can use the built-in setattr() function within the method, and that works as well, but requires that I change the syntax for the attribute to a string before passing it into the method, and I much prefer explicitly referring to the attribute. Finally, I'd really just like to understand what is going on here.
EDIT: This is the working code, based on the hint from rdvdev2. I just needed to reference self to set the instance's value:
class Test:
def __init__(self, attribute):
self.attribute = attribute
def change_attrib(self, attribute, value):
if attribute + value < 0: attribute = 0
else:
attribute += value
self.attribute = attribute
print(str(attribute) + ' This is inside the method')
Also thanks to kindall for the great explanation of what was happening.
And a final expansion: The code above actually only works if the attribute is named attribute. I think kindall had the better grasp of what I needed here; in order for the function to be used to modify any attribute of the object, I need some way to reference the needed attribute. Since python appears to be passing a value instead of a reference, I have to get the reference somehow, and the least impactful way to my existing code appears to be using get/setattr....so I broke out regex and changed 160+ references.
Upvotes: 2
Views: 8131
Reputation: 184191
When you pass in test1.attribute
to change_attrib()
, the value of attribute
inside the method is not a pointer to test1.attribute
that can be used to change its value. It is the integer 10. You then add the parameter value
to attribute
, yielding attribute
equal to 20. Then the method ends and the value of attribute
goes away. The value of test1.attribute
never changed because you never changed it.
If you want your method to modify any attribute, you can pass its name as a string. You can then use getattr()
and setattr()
to get and set the attribute.
def change_attrib(self, name, value):
attribute = getattr(self, name)
if attribute + value < 0: attribute = 0
else:
attribute += value
print(str(attribute) + ' This is inside the method')
setattr(self, name, attribute)
test1.change_attrib("attribute", 10)
Upvotes: 2
Reputation: 11
In the class method you are modifying the attribute
variable, wich is a function argument. If you want to modify the object attribute you have to access self.attribute
Upvotes: 0