0

An object has a list of dictionaries:

self.aggregator = []
self.aggregator.append({'type': 'Log', 'entry': logline1})
self.aggregator.append({'type': 'Log', 'entry': logline2})
print self.aggregator

Everything is OK so far as seen:

[{'type': 'Log', 'entry': 'Content of logline 1'}, {'type': 'Log', 'entry': 'Content of logline 2'}]

Then I try to reset the contents of this list using different approaches:

self.aggregator = []
del self.aggregator

Now when I print the list it is empty:

[]

But when I check the length of the list, it still has its old size:

>> print len(self.aggregator)
2

Why is this possible, did I miss anything?

=== Update

Complete demo code below. I figured out what causes the error. It's the line with "del self.aggregator". When I delete this line, everything works as expected. Tested this behaviour on OS X with Python 2.7.10 and Python 2.7.11 (32bit) on Windows 7.

class Checker(object):

    aggregator = []
    aggregator_max = 10

    def __init__(self):
        pass

    def finalizeAggregator(self):
        string = "\n".join([attribute['string'] for attribute in self.aggregator])

        # Check for a match on all the aggregator content
        match_result = self.checkString(string,"")

        if match_result:
            # If match has been found, check the aggregator contents one by one
            for element in self.aggregator:
                self.checkString(element["string"],
                                 element["module"],
                                 use_aggregator=False)

        # Clear aggregator
        print self.aggregator
        self.aggregator = None
        self.aggregator = []
        del self.aggregator
        print self.aggregator
        print len(self.aggregator)

    def checkString(self, string, module, use_aggregator=False):

            # If aggregator should be used
            if use_aggregator:
                print "USING aggregator"

                # As long as the aggregator is not full
                if len(self.aggregator) <= self.aggregator_max:

                    #if string not in self.aggregator:
                    # Add element to aggregator
                    self.aggregator.append({"string": string,
                                            "module": module})

                # Process aggregator if full
                if len(self.aggregator) >= self.aggregator_max:
                    self.finalizeAggregator()

                # Otherwise return
                else:
                    return False

            else:
                print "NOT using aggregator"

            if "evil" in string:
                print "WARNING!!!"

if __name__ == '__main__':
    checker = Checker()
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is evil', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
    checker.checkString('This is benign', 'test', use_aggregator=True)
JohnGalt
  • 419
  • 1
  • 5
  • 16
  • 1
    Could you post a minimal, verifyable example? I tried to verify the described behaviour with a little test-class but I cannot reproduce your issue. Please post more code. – JDurstberger Jan 20 '16 at 13:41
  • 7
    I can't reproduce this... It works for me and furthermore after del it throws a NameError exception of object undefined. – Idos Jan 20 '16 at 13:47
  • 1
    Please post your code. You should not be able to use self.aggregator when you delete it using del keyword. – mitghi Jan 20 '16 at 13:47
  • 1
    I think you are missing something. It works perfectly. – Adem Öztaş Jan 20 '16 at 13:49
  • 2
    This line `del self.aggregator` should delete the variable, so when you ask for its value you should get an error. More code please. – tglaria Jan 20 '16 at 13:57
  • Posted a code sample – JohnGalt Jan 20 '16 at 14:07
  • 1
    @JohnGalt: The `del` is not the real error, it just reveals the real error. See [my answer](http://stackoverflow.com/a/34902561/364696). – ShadowRanger Jan 20 '16 at 14:22

1 Answers1

2

You defined aggregator as a class attribute; until you assign to self.attribute directly, it's a shared class attribute. But when you do:

self.aggregator = None

that auto-vivifies an instance attribute. So after that point, it's a completely different variable that shadows (hides) the class attribute. Except when you do del self.aggregator, you delete the instance attribute, and unshadow the class attribute, seeing the original (shared) value.

If the goal is to have an instance attribute, use an instance attribute. Change:

class Checker(object):

    aggregator = []
    aggregator_max = 10

    def __init__(self):
        pass

to:

class Checker(object):
    aggregator_max = 10

    def __init__(self):
        self.aggregator = []

and it will be solely an instance variable (just don't del the actual attribute if you intend to use it later, that's nonsensical; assigning the empty list is fine though).

Note that there is a difference between assigning the empty list (replacing the reference held in self.aggregator) and deleting the contents of the list referenced by self.aggregator (del self.aggregator[:] or in Py3.3+, self.aggregator.clear()) in the case where there might be more than one reference to the list referenced by self.aggregator. Take a look at Clearing Python lists for more details.

Community
  • 1
  • 1
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271