0

I am having some sort of issue with list aliasing or variable assignment in Python 2.7. I will show you a minimal example. There are two different results with and without the assertion, and I don't know why/how the assertion affects this.

Somehow it is overwriting an attribute when I append something to the object_list shown below:

class Object1(object):                                                                                 

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

    def add_thing(self, thing):                                                                                          
        # this next line makes all the difference
         assert thing.name not in [thing.name for thing in self.object_list], 'agent id already exists, use another one'
        self.object_list.append(thing)

class Thing(object):

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name

Here is a minimal example:

With the assertion

mfd = Object1()
myAgent = Thing('blah')
myAgent_2 = Thing('blew')

mfd.add_thing(myAgent)
mfd.add_thing(myAgent_2)

print mfd.object_list

>> [blah, blah]

Without the assertion

mfd = Object1()
myAgent = Thing('blah')
myAgent_2 = Thing('blew')

mfd.add_thing(myAgent)
mfd.add_thing(myAgent_2)

print mfd.object_list

>> [blah, blew]

Can someone please explain to me how the assertion above is affecting this? Thank you.

5
  • Why are you using the same name for the loop variable in the list comprehension as you are for the parameter? Even if that wasn't causing this error (it wouldn't in 3.x), it seems deliberately confusing. Commented Jan 30, 2018 at 20:12
  • 2
    [thing.name for thing in self.object_list] ... now has thing= self.object_list[-1] ... instead just do [other.name for other in self.object_list] Commented Jan 30, 2018 at 20:14
  • 3
    @JoranBeasley better still, use a set comprehension, which doesn't have leaky scope in Python 2 (although i'd still not use the same name), and at least then you aren't doing two O(n) operations in a row. Commented Jan 30, 2018 at 20:18
  • 1
    Moral of the story: use Python 3 :) Commented Jan 30, 2018 at 20:19
  • 1
    or define your class so you can just do new_thing not in self Commented Jan 30, 2018 at 20:20

1 Answer 1

6

In python 2, implicit assignments made in the scope of a list comprehension leak outside of the comprehension to the caller's scope.

So in your example, by executing the assert, thing's value changes and the final value of thing is the final thing from object_list. Therefore, without assert, you are appending a different thing than after the assert has been executed.

Sign up to request clarification or add additional context in comments.

2 Comments

wow that seems like a serious design flaw. and this goes away in python 3?
yes python 3 does not have this. IIRC it is an artifact from a c optimization

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.