Notify parent instance about property change - python

I have these two classes:
class Status(object):
def __init__(self):
self._message = ''
#property
def message(self):
return self._message
#message.setter
def message(self, value):
self._message = value
class Buddy(object):
def __init__(self, name):
self.name = name
self.status = Status()
def status_updated(self):
# this should be called when self.status.message is changed
and I use them like this:
buddy = Buddy('John')
buddy.status.message = 'Hello world!' # this should call Buddy.status_updated
I want Buddy.status_updated to be called when I modify the message property of Status. How to achieve this?

You'll have to store a reference back to the parent; python values do not track where they are stored (there can be multiple places that refer to your Status() instances):
class Status(object):
def __init__(self, parent=None):
self._message = ''
self._parent = parent
#property
def message(self):
return self._message
#message.setter
def message(self, value):
self._message = value
if self._parent is not None:
self._parent.status_updated()
class Buddy(object):
def __init__(self, name):
self.name = name
self.status = Status(self)
def status_updated(self):
# this should be called when self.status.message is changed

In each case you need somehow to register you want to "listen" to other's object property changes. I would suggest simple solution like this:
class Status(object):
def __init__(self, on_message_change=None):
self._message, self._on_message_change = '', on_message_change
#property
def message(self):
return self._message
#message.setter
def message(self, value):
if self._on_message_change:
self._on_message_change(self._message, value)
self._message = value
class Buddy(object):
def __init__(self, name):
self.name = name
self.status = Status(self.status_updated)
def status_updated(self, old_value, new_value):
print("status changed '%s' -> '%s'" % (old_value, new_value))
b = Buddy("someone")
b.status.message = "init"
b.status.message = "new"
output is:
status changed '' -> 'init'
status changed 'init' -> 'new'

Related

Python: Method in subclasses that has different assignments

I have a method run() in subclasses, which has an API POST request in each subclass, gets data from the POST request, and assigns an ID from this data to self._id. Now I would like to get a description too. However, description is returned only in the API request self._api_obj.trigger(...) in SubClassB.run, not in the API request self._api_obj.trigger_run(...) in SubClassA.run. For SubclassA I need a separate API request to get the description.
I tried the following, but I don't think it's a good idea to assign 2 attributes in SubClassB.run, but only 1 attribute in SubClassA.run. Right? Since from my understanding, the same method in subclasses should have the same behavior (Just different implementation).
class SuperClass:
def __init__(self):
self._id = None # Assigned in run()
self._description = None
#property
def description(self):
raise NotImplementedError
def run(self, *args):
raise NotImplementedError
#property
def id(self):
return self._id
class SubClassA(SuperClass):
def __init__(self):
super().__init__()
self._api_obj = ApiObj1()
#property
def description(self):
if not self._description:
_result = self._api_obj.get_data()
self._description = _result["description"]
return self._description
def run(self, *args):
_result = self._api_obj.trigger_run(foo="foo")
self._id = _result["RunId"]
class SubClassB(SuperClass):
def __init__(self):
super().__init__()
self._api_obj = ApiObj2()
#property
def description(self):
return self._description
def run(self, *args):
_result = self._api_obj.trigger(foo="foo", bar="bar", arg1="arg1", arg2="arg2")
self._id = _result["data"]["id"]
self._description = _result["data"]["description"]
Is there a better way to add assignment to self._description? Or some other solution to include description?
Here is a solution I thought of: Creating a private method _run(), that will set and return the complete result of the run. The method SubClassB.get_description() will be able to get any required info from it. However, SubClassA.get_description() will get the description from the API.
class SuperClass:
def __init__(self):
self._id = None # Assigned in run()
self._run_result_info = None # Assigned in _run()
def _run(self, *args):
raise NotImplementedError
def run(self):
raise NotImplementedError
def get_description(self):
raise NotImplementedError
#property
def id(self):
return self._id
class SubClassA(SuperClass):
def __init__(self):
super().__init__()
self._api_obj = ApiObj1()
def _run(self):
self._run_result_info = self._api_obj.trigger_run(foo="foo")
return self._run_result_info
def run(self):
_result = self._run()
self._id = _result["RunId"]
def get_description(self):
_result = self._api_obj.get_data()
_description = _result["description"]
return _description
class SubClassB(SuperClass):
def __init__(self):
super().__init__()
self._api_obj = ApiObj2()
def _run(self):
self._run_result_info = self._api_obj.trigger(foo="foo", bar="bar", arg1="arg1", arg2="arg2")
return self._run_result_info
def run(self):
_result = self._run()
self._id = _result["data"]["id"]
def get_description(self):
return self._run_result_info["data"]["description"]
Another option: Not keeping the result of the run, since it's not always needed. Just have a property description that will get the description through an API request only in cases it's needed. It means that we will need an API request in SubClassB to get the description too, instead of having the data available after SubClassB.run, but at least we're not keeping unneeded data, if it's not requested. We keep it only if it's requested.
class SuperClass:
def __init__(self):
self._id = None # Assigned in run()
self._description = None
#property
def description(self):
raise NotImplementedError
def run(self):
raise NotImplementedError
#property
def id(self):
return self._id
class SubClassA(SuperClass):
def __init__(self):
super().__init__()
self._api_obj = ApiObj1()
#property
def description(self):
if not self._description:
_result = self._api_obj.get_data()
self._description = _result["description"]
return self._description
def run(self, *args):
_result = self._api_obj.trigger_run(foo="foo")
self._id = _result["RunId"]
class SubClassB(SuperClass):
def __init__(self):
super().__init__()
self._api_obj = ApiObj2()
#property
def description(self):
if not self._description:
_result = self._api_obj.get_info()
self._description = _result["description"]
return self._description
def run(self, *args):
_result = self._api_obj.trigger(foo="foo", bar="bar", arg1="arg1", arg2="arg2")
self._id = _result["data"]["id"]

Python, delete a list item from class method

I have my class.
I want to create a method inside to delete a list item by code attribute.
class MyClass(Base):
def __init__(self, code, name):
self.__code = code
self.__name = name
#property
def code(self):
return self.__code
#property
def name(self):
return self.__name
#code.setter
def code(self, new_code):
self.__code=new_code
def __repr__(self):
x = f"Code: {self.__code} and Name:{self.__name}"
return(x)
def __deleteitem__(self, code):
print("The code: {self.__code} was deleted")
list=[]
list.append(MyClass(1234,"Aijio"))
list.append(MyClass(123,"Anodnd"))
list.append(MyClass(1236,"Jfifi"))
list.append(MyClass(1238,"Roberto"))
print(list)
lista.deleteitem(123)
How I can create a method who deletes the code that I send?
Regards
You can try this below :
class MyClass(Base):
def __init__(self, code, name):
self.__code = code
self.__name = name
#property
def code(self):
return self.__code
#property
def name(self):
return self.__name
#code.setter
def code(self, new_code):
self.__code=new_code
def __repr__(self):
x = f"Code: {self.__code} and Name:{self.__name}"
return(x)
def __deleteitem__(self, code):
# Logic for deletion
for obj in list:
if obj.code == code:
list.remove(obj)
print("The code: "+code+" was deleted")
list=[]
list.append(MyClass(1234,"Aijio"))
list.append(MyClass(123,"Anodnd"))
list.append(MyClass(1236,"Jfifi"))
list.append(MyClass(1238,"Roberto"))
myclass = MyClass(None, None)
myclass.__deleteitem__(123)

Derived class doesn't recognise arguments of method from parent

I'm trying to make a set of functions to operate easily through some data.
The problem I'm facing is: it seems to recognize and use methods from the parent class, except one: show(), giving me errors about unexpected arguments.
Here's a sample of the classes:
from treelib import Tree, Node
class Join(Tree):
def __init__(self, id, desc, childs=(), *args, **kwargs):
Tree.__init__(self, *args, **kwargs)
self.id = id
self.desc = desc
self.value = None
self.parent = None
self.childs = None
self.create_node(tag=desc, identifier=id)
for i in childs:
self.paste(self.id, i)
def getSons(self):
sons = self.children(self.id)
return sons
def getID(self):
return self.id
def getDesc(self):
return self.desc
def show(self):
self.show(key=lambda x: x.tag, reverse=True, line_type='ascii-em')
class Get(Tree):
def __init__(self, id, desc, primitive, *args, **kwargs):
Tree.__init__(self, *args, **kwargs)
self.id = id
self.desc = desc
self.parent = None
self.primitive = primitive
self.create_node(tag=desc, identifier=id, data=primitive)
def getID(self):
return self.id
def getDesc(self):
return self.desc
def show(self):
self.show(key=lambda x: x.tag, reverse=True, line_type='ascii-em')
class Primitive():
def __init__(self, value):
self.value = value
def getValue(self):
return self.value
def show(self):
pass
#print '\t -> ' + str(self.value)
If, for example, I do this on another .py
prim = Primitive(0)
get1 = Get("get1", "Some random thing", prim)
get1.show()
it tells me that key is an unexpected argument. I even checked the library's .py file, the argument is there:
def show(self, nid=None, level=ROOT, idhidden=True, filter=None,
key=None, reverse=False, line_type='ascii-ex'):
The create_node() method works just fine! That's what's weird. Any suggestions?
I'm using treelib in Python 2.7
Your method show() calls itself:
def show(self):
self.show(key=lambda x: x.tag, reverse=True, line_type='ascii-em')
Removed it in Get and change it in Join to:
def show(self):
super(Join, self).show(key=lambda x: x.tag, reverse=True, line_type='ascii-em')

How to make this code work in python 2

class Event(metaclass=ABCMeta):
def __init__(self):
self.type = self.get_full_type()
#classmethod
def get_full_type(cls):
return None
def as_dict(self):
return self.__dict__
class BaseEvent(Event, metaclass=ABCMeta):
SUB_TYPE = ''
#classmethod
def get_base_type(cls):
return super().get_full_type()
#classmethod
def get_full_type(cls):
base_type = cls.get_base_type()
if base_type:
return '.'.join([base_type, cls.SUB_TYPE])
else:
return cls.SUB_TYPE
Here you can see my attempt to make class that represents some abstract event. What is crucial here is the ability to distinguish event types. So every event has it's type and it's base type. Full type is base type + subtype.
This gives the ability to define new event type like this
class MockEvent(BaseEvent):
SUB_TYPE = 'mock'
def __init__(self, some_object):
super(self.__class__, self).__init__()
self.some_object = some_object
So the full type is mirroring the class hierarchy ClassA.ClassB.ClassC etc. I think you get the point.
Unfortunately this is not working with python 2
class Event(object):
__metaclass__ = ABCMeta
SUB_TYPE = None
def __init__(self):
self.type = self.get_full_type()
#classmethod
def get_base_type(cls):
return None
#classmethod
def get_full_type(cls):
base_type = cls.get_base_type()
if base_type:
return '.'.join([base_type, cls.SUB_TYPE])
else:
return cls.SUB_TYPE
def as_dict(self):
return self.__dict__
class BaseEvent(Event):
__metaclass__ = ABCMeta
SUB_TYPE = ''
#classmethod
def get_base_type(cls):
return super(cls.__class__, cls).get_full_type()
File "/opt/leos/code/event_service/events/EventBus.py", line 38, in
get_base_type
return super(cls.class, cls).get_full_type()
AttributeError: 'super' object has no attribute 'get_full_type'
How can I make this work?
class Event(object):
__metaclass__ = ABCMeta
def __init__(self):
self.type = self.get_full_type()
#classmethod
def get_full_type(cls):
return None
def as_dict(self):
return self.__dict__
class BaseEvent(Event):
__metaclass__ = ABCMeta
SUB_TYPE = None
#classmethod
def get_full_type(cls):
super_type = cls.get_super_type()
base_type = super_type.get_full_type()
if base_type:
return '.'.join([base_type, cls.SUB_TYPE])
else:
return cls.SUB_TYPE
#classmethod
def get_super_type(cls):
return cls.__base__
I needed to get the base type automatically. Without mentioning current class in super(currectClass, self) So I used cls.base and it's working ok.

Expanding the class from object

I have some classes:
class Window(object):
def __init__(self, name):
self.wind_name = name
def getWindowName(self):
return 'wnd' + self.wind_name
class Control(object):
def __init__(self, name, wnd):
self.contrl_name = name
setattr(self, 'getWindowName', wnd.getWindowName)
setattr(self, 'wind_name', wnd.wind_name)
def getControlName(self):
return (self.getWindowName(), 'unk' + self.contrl_name)
class Button(Control):
def __init__(self, name, wnd):
super(Button, self).__init__(name, wnd)
def getControlName(self):
return (self.getWindowName(), 'btn' + self.contrl_name)
wnd = Window('MyApp')
btnOK = Button('OK', wnd)
btnOK.getControlName() # work ok., return ('wndMyApp', 'btnOK')
btnOK.wind_name = 'NewApp'
btnOK.getControlName() # does not work properly., return ('wndMyApp', 'btnOK')
How can I extend the class Control|Button from the object of class Window to access the functions getWindowName and field wind_name in objects btnOK?
Is there a way without creating a field self.wnd = wnd in class Control, or add method setWindowName in Window...?
I can not inherit class Control from the class Window! This is not logical.
Python allows inheriting from multiple classes, i.e.
class Button(Control, Window):
...
But in this case you should know exactly what you are doing (speaking of Pythons Method Resolution Order (MRO)). I'd recommend reading this small book: Python Attributes and Methods.
You can use property for attributes
class Window(object):
def __init__(self, name):
self.wind_name = name
def getWindowName(self):
return 'wnd' + self.wind_name
class Control(object):
def __init__(self, name, wnd):
self.contrl_name = name
self.wnd = wnd
setattr(self, 'getWindowName', wnd.getWindowName)
def get_wind_name(self):
return self.wnd.wind_name
def set_wind_name(self, v):
self.wnd.wind_name = v
wind_name = property(get_wind_name, set_wind_name)
def getControlName(self):
return (self.getWindowName(), 'unk' + self.contrl_name)
class Button(Control):
def __init__(self, name, wnd):
super(Button, self).__init__(name, wnd)
def getControlName(self):
return (self.getWindowName(), 'btn' + self.contrl_name)
wnd = Window('MyApp')
btnOK = Button('OK', wnd)
print btnOK.getControlName() # work ok., return ('wndMyApp', 'btnOK')
btnOK.wind_name = 'NewApp'
print btnOK.getControlName()

Categories