Home > database >  Prevent Class from updating attributes if None
Prevent Class from updating attributes if None

Time:08-29

I have a Device class like so, that I pass data from a websocket to. The data from the websocket isn't always the same, so what I want to do is only update the value of each item in the class, if it is not None.

As an example, the message_data might be the below on the first message

{
  "ssdp": "foo",
  "ip": "bar"
}

and then the following on the second:

{
  "ssdp": "foo",
}

My code is below:

Script

                if self._device is None:
                    self._device = Device(message_data)

                device = self._device.update_from_dict(message_data)

Class

@dataclass
class Info:
    ssdp: str
    ip: str

    @staticmethod
    def from_dict(data):
        return Info(
            ssdp=data.get('SSDP'),
            ip=data.get('ip'),
        )


class Device:
    def __init__(self, data):
        self.info = None
        self.update_from_dict(data)

    def update_from_dict(self, data):
        self.info = Info.from_dict(data)
        return self

Unfortunately at the moment, this is returning

{'info': Info(ssdp='foo', ip='bar')}

and then

{'info': Info(ssdp='foo', ip=None)}

CodePudding user response:

@dataclass
class Info:
    ssdp: str
    ip: str

    @staticmethod
    def update(data, info):
        return Info(
            ssdp=data.get('SSDP', info.ssdp),
            ip=data.get('ip', info.ip),
        )


class Device:
    def __init__(self, data):
        self.info = Info(None, None)
        self.update_from_dict(data)

    def update_from_dict(self, data):
        self.info = Info.update(data, self.info)

This is how I would go about it. I make use of the default value to the dict.get() and then consequently pass the current info through to the update method. It is annoying that you have to initialise the info as Info(None, None), but someone else might have a better approach :)

CodePudding user response:

You can try with this readapted version of your code:

from dataclasses import dataclass 

@dataclass
class Info:
    ssdp: str = None
    ip: str = None

    def from_dict(self, data):
        self.ssdp = data['ssdp'] if 'ssdp' in data.keys() else self.ssdp[0],
        self.ip = data['ip'] if 'ip'  in data.keys()  else self.ip[0],
        return self 



class Device:
    def __init__(self, data):
        self.info = Info()
        self.update_from_dict(data)

    def update_from_dict(self, data):
        self.info.from_dict(data)
        return self

For test purposes :

if __name__ == "__main__":

    data = {
    "ssdp": "foo",
    "ip": "bar"
    }

    msg_data = {
    "ssdp": "test",
    }

    device = Device(data)
    print("before: ", device.info)

    device = device.update_from_dict(msg_data)
    print("after: ", device.info)

output :

before:  Info(ssdp=('foo',), ip=('bar',))
after:  Info(ssdp=('test',), ip=('bar',))

CodePudding user response:

Try with these redefined new classes:

from dataclasses import dataclass, fields, replace
from operator import attrgetter

@dataclass
class Info:
    ssdp: str = None
    ip: str = None


class Device:
    def __init__(self, data):
        self.info = Info()
        self.update_from_dict(data)

    def update_from_dict(self, data):
        keys = set(map(attrgetter('name'), fields(self.info)))
        data = {k: v for k, v in data.items() if k in keys}
        self.info = replace(self.info, **data)
        return self

This solution is using the replace function, which basically creates a new object of the same type as the input object, replacing fields with values from changes.

  • Related