Search code examples
pythonlistgarbage

How to Instantiate a Completely Fresh Temporary Object that Contains Lists


I'm currently using Python to parse CAN database files. I ran into a problem with lists during implementation and gave it a quick patch that makes it work, but it's kind of ugly and seems as if there's a more elegant solution.

I have defined an object CAN database and one of it's methods takes the file to be parsed, which contains definitions of messages in the database. I loop through each line in the file and when I come across a line indicating a message description, I create a temporary variable referencing an object I've defined for CAN messages, some of the members of which are lists. I put elements in these lists with a method based on the next handful of lines in the file.

Now when I'm done with this temporary object, I add it to the CAN database object. Since I no longer need the data referenced by this variable, I assign the value None to it and reinstantiate the clean slate variable on the next iteration through that detects a message descriptor. Or that was the plan.

When I go through the next iteration and need to use this variable, I add some values to these lists and find that they're not actually empty. It seems that despite assigning the variable to reference None the values in the lists persisted and were not cleaned up.

Below you can see my solution which was to stack more methods on specifically to get rid of the persisting list elements.

Here's some relevant portions of the file:

Parsing Loop

for line in file:
    line = line.rstrip('\n')
    line_number += 1  # keep track of the line number for error reporting

    if line.startswith("BU_:"):
        self._parseTransmittingNodes(line)

    elif line.startswith("BO_"):
        can_msg = self._parseMessageHeader(line).ResetSignals().ResetAttributes()
        building_message = True

    elif line.startswith(" SG_") and building_message:
        can_msg.AddSignal(self._parseSignalEntry(line))
        # can_msg.updateSubscribers()

    elif line == "":
        if building_message:
            building_message = False
            self._messages += [can_msg]
            can_msg = None

Reset Methods

def ResetSignals(self):
    """
    Flushes all the signals from the CANMessage object.
    """
    self._signals = []
    return self

def ResetAttributes(self):
    """
    Flushes all the attributes from the CANMessage object.
    """
    self._attributes = []
    return self

How can I make this variable a fresh object every time? Should I have a method that clears all of it's internals instead of assigning it None like the IDispose interface in C#?

EDIT: Here's the full source for the CANMessage object:

class CANMessage:
    """
    Contains information on a message's ID, length in bytes, transmitting node,
    and the signals it contains.
    """
    _name = ""
    _canID = None
    _idType = None
    _dlc = 0
    _txNode = ""
    _comment = ""
    _signals = list()
    _attributes = list()
    _iter_index = 0
    _subscribers = list()

    def __init__(self, msg_id, msg_name, msg_dlc, msg_tx):
        """
        Constructor.
        """
        self._canID = msg_id
        self._name = msg_name
        self._dlc = msg_dlc
        self._txNode = msg_tx

    def __iter__(self):
        """
        Defined to make the object iterable.
        """
        self._iter_index = 0
        return self

    def __next__(self):
        """
        Defines the next CANSignal object to be returned in an iteration.
        """
        if self._iter_index == len(self._signals):
            self._iter_index = 0
            raise StopIteration
        self._iter_index += 1
        return self._signals[self._iter_index-1]

    def AddSignal(self, signal):
        """
        Takes a CANSignal object and adds it to the list of signals.
        """
        self._signals += [signal]
        return self

    def Signals(self):
        """
        Gets the signals in a CANMessage object.
        """
        return self._signals

    def SetComment(self, comment_str):
        """
        Sets the Comment property for the CANMessage.
        """
        self._comment = comment_str

        return self

    def CANID(self):
        """
        Gets the message's CAN ID.
        """
        return self._canID

    def AddValue(self, value_tuple):
        """
        Adds a enumerated value mapping to the appropriate signal.
        """
        for signal in self:
            if signal.Name() == value_tuple[0]:
                signal.SetValues(value_tuple[2])
                break
        return self

    def AddAttribute(self, attr_tuple):
        """
        Adds an attribute to the message.
        """
        self._attributes.append(attr_tuple)
        return self

    def ResetSignals(self):
        """
        Flushes all the signals from the CANMessage object.
        """
        self._signals = []
        return self

    def ResetAttributes(self):
        """
        Flushes all the attributes from the CANMessage object.
        """
        self._attributes = []
        return self

    def Name(self):
        return self._name

    def TransmittingNode(self):
        return self._txNode

    def DLC(self):
        return self._dlc

Solution

  • The problem you're seeing is because you used class attributes instead of instance attributes. If you move the initialization of the attributes you don't pass to __init__ from class scope into __init__, each instance will have its own set of lists.
    Here's what that would look like:

    class CANMessage:
        """
        Contains information on a message's ID, length in bytes, transmitting node,
        and the signals it contains.
        """
    
        def __init__(self, msg_id, msg_name, msg_dlc, msg_tx):
            """
            Constructor.
            """
            self._canID = msg_id
            self._name = msg_name
            self._dlc = msg_dlc
            self._txNode = msg_tx
            self._name = ""
            self._canID = None
            self._idType = None
            self._dlc = 0
            self._txNode = ""
            self._comment = ""
            self._signals = list()
            self._attributes = list()
            self._iter_index = 0
            self._subscribers = list()
    
        # the rest of the class is unchanged, and not repeated here...