I have an instance variable from a class and I want to execute some code when there is a change in my variable.
I'm aware of the property and Observer pattern event handling but I don't think it helps in my case.
Example:
class Thing:
def __init__(self):
self.thing = []
self.thing2 = ""
def code_that_executes(self):
self.thing2 = self.thing[0]
s = Thing()
s.thing.append("Something") #The event
You can implement setattr on your class. Here is an example:
class A:
def __init__(self):
self.A = 5
def __setattr__(self, name, value):
if name == "A":
print("A has changed to: {0}".format(value))
Now when you have an object `foo = Foo()` and call `foo.bar = 5` you get the result:
bar changed to 5
See https://docs.python.org/3/reference/datamodel.html#object.setattr
Note:This will print during the init call as well.
Related
I need to access the value of an attribute defined at the parent class inside an inner class, here's the code:
class main(object):
def __init__(self):
self.session_id = None
self.devices = self.Devices(self.session_id)
class Devices(object):
def __init__(self, session_id):
self.session_id = session_id
And here's how I would like to use it:
>>> m = main()
>>> m.session_id = 1
>>> m.session_id
1
>>> m.devices.session_id
>>>
My expectation is that m.devices.session_id will always have the exact same value as m.session_id. I understand that at this point when I instantiate the inner class the session_id value is passed down as None because that's how it was initiated but I'm not sure how I can keep both values the same without doing something very ugly like:
m.devices.session_id = m.session_id
outside the class code.
How can I accomplish that inside the class itself ?
The other answer works, but I think this is a better design: lose the nested class, and add a getter on the device object to lookup a backref:
class Main(object):
def __init__(self):
self.session_id = None
self.devices = Devices(main_obj=self)
class Devices(object):
def __init__(self, main_obj):
self.main_obj = main_obj
...
#property
def session_id(self):
return self.main_obj.session_id
The difference here is that you're not storing the same data twice, so they can not get out of sync - there is only one "source of truth" for the session_id (on main object).
In the earlier answer, the data is actually stored in two different namespaces and will get out of sync as easily as m.devices.session_id = 123.
You can do it like this:
class main(object):
def __init__(self):
self._session_id = None
self.devices = self.Devices(self._session_id)
#property
def session_id(self):
return self._session_id
#session_id.setter
def session_id(self, value):
self._session_id = self.devices.session_id = value
class Devices(object):
def __init__(self, session_id):
self.session_id = session_id
I am trying to pass a value from one function in a class to another function in a class. Below is some simplified code of what I'm trying to achieve.
class test:
def __init__(self):
self.differentvalue = 0
def set(self, value):
print(value)
self.differentvalue = value #this is not the same value as defined above - i.e. this is a new variable created in foo class i believe
class foo:
def __init__(self):
test.set(self, 5)
if __name__ == '__main__':
foo()
I do not want __init__ to be called so test().set(5) is not an option.
Cheers,
Sean
You have two options
Option #1, best option if you need to keep a different context for differtvalue for each instance of Test
class Test:
def __init__(self):
self.differentvalue = 0
def set(self, value):
self.differentvalue = value
class foo:
def __init__(self):
test = Test()
test.set(5)
Option #2, best if you need to keep the latest value for differentvalue across all Test classes
class Test:
__DIFFERENTVALUE = 0
def __init__(self):
pass
#staticmethod
def set(value):
Test.__DIFFERENTVALUE = value
class foo:
def __init__(self):
Test.set(5)
You could define a class variable with a value of None, then upon calling the setter for the first time, assign a value to it. Further calls to the setter will not change the value.
In the following example, an __init__ method is not required in Test.
class Test:
differentvalue = None
#classmethod
def set(cls, value):
if value is not None and Test.differentvalue is None:
Test.differentvalue = value
class foo:
def __init__(self):
Test.set(5)
if __name__ == '__main__':
foo()
print(Test.differentvalue)
Test.set(12)
print(Test.differentvalue)
output:
5
5 # the value did not change
I have a parent class with 3 items in it. I am trying to create a child class that when called updates a set item in the parent class.
class NOS:
def __init__(self):
self.Bike = 0
self.car = 0
self.plane = 0
class buy(NOS):
def __init__(self, mode):
NOS.__init__(self)
self.mode = mode
def buy_comp(self, value):
self.mode += value
if i called it like below
a = buy('bike')
a.buy_comp(4)
I am trying to get to a situation where bike would equal 4. The above did not work. Neither did the below where i tried to use buy as a function instead of a class.
def buy(self, mode, value):
self.mode += value
a= NOS()
a.buy('bike', 5)
Here i got the error - AttributeError: 'NOS' object has no attribute 'bike'
In the first example you posted, your child class "buy" is not actually a child class, because it is not inheriting from "NOS".
Not exactly sure what you're trying to achieve. Maybe this is helpful?
class Parent:
def __init__(self):
self.foo = "Parent Foo"
class Child(Parent):
def __init__(self):
Parent.__init__(self)
def set_foo(self, new_foo):
self.foo = new_foo
child = Child()
print(child.foo)
child.set_foo("New Foo")
print(child.foo)
Output:
Parent Foo
New Foo
EDIT - Oh, I think I get it now. Something like this maybe?
class NOS:
def __init__(self):
self.bike = 0
self.car = 0
self.plane = 0
class Buy(NOS):
def __init__(self, item_name):
NOS.__init__(self)
self.item_name = item_name
def buy_comp(self, amount):
try:
old_value = getattr(self, self.item_name)
except NameError:
# No such item exists
pass
else:
setattr(self, self.item_name, old_value + amount)
a = Buy("bike")
print(a.bike)
a.buy_comp(4)
print(a.bike)
However, I think that if you're relying on getattr and setattr, there's bound to be a better way. I have a feeling that this may be an instance of an XY problem. Can you tell us more about the actual use case? I'm sure there's a more elegant solution you could benefit from.
I have a nested class setup like the code snippet bellow.
class test:
class child:
some_variable = None
When I try to call this code from another .py file like bellow
from testing import test
t = test()
t.child.some_variable ="123"
t = test()
print(t.child.some_variable)
I get the output
123
I expected to get None, or at least an error message. I have tried to solve it with the following approach but the problem persists with the same output.
class test:
def __init__(self):
self.child()
class child:
some_variable = None
def __init__(self):
self.some_variable = ""
How can I initiate a new child class when I am calling the parent class?
By don't having it as an inner class, but as a separate class and then an instant attribute:
class child_class:
def __init__(self):
self.some_variable = None
class test:
def __init__(self):
self.child = child_class()
t = test()
t.child.some_variable = "123"
t = test()
print(t.child.some_variable) # prints None
Or alternative you can have a inner class, but still you have to create an instance attribute:
class test:
class child_class:
def __init__(self):
self.some_variable = None
def __init__(self):
self.child = self.child_class()
t = test()
t.child.some_variable = "123"
t = test()
print(t.child.some_variable) # also prints None
I'm having some trouble with changing the value of a class at runtime and then instantiating it into an object, then storing that object inside of another class and putting that into python dictionary.
Here is a small code snippet I wrote to illustrate the problem:
import unittest
class cls1(object):
def __init__(self, obj):
self.obj = obj
class cls2(object):
def __init__(self):
self.var = 1
class Testdict(unittest.TestCase):
def __init__(self):
self.objs = dict()
def runTest(self):
obj2 = cls2()
obj1 = cls1(cls2())
self.objs["test1"] = obj1
self.assertEqual(self.objs["test1"].obj.var, 1)
cls2.var = 2
self.assertEqual(cls2.var, 2)
obj1 = cls1(cls2())
self.objs["test2"] = obj1
self.assertEqual(self.objs["test1"].obj.var, 1)
self.assertEqual(self.objs["test2"].obj.var, 2)
if __name__ == "__main__":
d = Testdict()
d.runTest()
Why would cls2 not instantiate with having it's var equal to 2?
I hope this question makes some sense.
What you're showing can't work. Ever.
class Cls2(object):
def __init__(self):
self.var = 1
That's an instance variable. It's not a class variable. You can't access that .var with Cls2.var That variable only exists within each unique instance of the class.
Cls2.var = 2
Does not change the self.var instance variable. That creates a new class variable in the Cls2 class.
You'd need to do something like this.
class Cls2(object):
default= 1
def __init__(self):
self.var = Cls2.default
Now you can do
Cls2.default= 2
And the rest of whatever it is you're doing should work.
Your test would work if cls2 didn't overwrite cls.var when it is instantiated.
Try this:
class cls2(object):
def __init__(self):
try:
self.var
except:
self.var = 1
The try statement just checks to see if you've already set var.