2

I have to say that I don't really have a problem, as "everything works fine", but I struggle to understand why.

I'm writing a script in python/urwid. In my script I have a class ItemWidget, whose self._w is equal to an urwid.Pile of 3 widgets, each of which is a line of urwid.Text. Besides, the class ItemWidget has a property self.visibility, which is a list of three booleans, and a method self.rebuild(). The init function is

def __init__ (self, content,vis):
    self.content = content
    self.visibility = vis
    self.rebuild()
    self.__super.__init__(self._w)    

The method rebuild() rebuilds self._w depending on the values in self.visibility().

In the main() function of the script I have a variable

globalvisibility = [1,1,1]

and I create a list of (roughly 1000) instances of ItemWidget:

for content in abstracts:                               # (*)
    items.append(ItemWidget(content,globalvisibility))  #

In fact I used a variable and not

for content in abstracts.items():                        
    items.append(ItemWidget(content,[1,1,1]))            

only because later I will implement saving globalvisibility to a file. But I didn't think it would be useful for anything apart from this initialization.

Anyway, in the script I often take an instance of the ItemWidget and change its visibility via for example

item.visibility[2] = 0    # (**)

and similar. Up to this point everything works fine and as expected.

Here's my question. Why after creating the instances of ItemWidget via (*), changing the variable globalvisibility to, say, [0,1,1] affects all these instances?

I have to say I "discovered" it accidentally and it seems very useful (I thought that to change the visibility proerties of all instances of ItemWidget, I would have to loop through all of them), but - to me - extremely surprising. Especially considering that the operations (**) affect only single instances.

I would very much appreciate some explanation.

EDIT: The whole script is here: https://www.dropbox.com/s/a0a4a0asyi5lyxw/tescik.py?dl=0 and example database from which i create the instances if itemwidget is: https://www.dropbox.com/s/zuwbvggznst85ru/arxiv-2013-05-23.db?dl=0

In order to run it you'd need to modify line 58 to point to the database. To see the behaviour, scroll to some items and press enter - the abstracts will vanish only on these items, then press "show abstracts" - this will reset the behaviour for all items.

  • Because all of your instances are sharing the same list object. – Ashwini Chaudhary Feb 03 '15 at 21:45
  • 1
    Just to be clear about where the issue is: does the output of `a = [1,2,3]; b = a; a.append(4); print(b);` also surprise you? – DSM Feb 03 '15 at 21:45
  • @DSM: let me say it this way: what surprises me is that I can change the vis property via (**) in particular instances (different instances have different vis), but then when I set globalvisibility to something then they all reset to the same thing – Łukasz Grabowski Feb 03 '15 at 22:00

1 Answers1

2

Why after creating the instances of ItemWidget via (*), changing the variable globalvisibility to, say, [0,1,1] affects all these instances?

Because you passed globalvisibility when creating all of these instances. All of the instances store a reference to the same list in their vis attribute.

If you want to avoid this, copy the list, either in the __init__ method or when creating the instances:

def __init__ (self, content,vis):
    self.content = content
    self.visibility = vis[:]    # here
    # etc.

Or:

for content in abstracts:
    items.append(ItemWidget(content, globalvisibility[:]))  

The exact choice you make here depends on whether you will ever want the current behavior. If so, leave it out of __init__() and do the copy when making the instances.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • Thx Kindall, I understand that all the instances store a reference to the same list in their vis attribute. But then how come I can change vis in particular instances via (**). I mean I see the result on my screen and the instances clearly behave as if they have different vis. Only when I change globalvisibility they all somehow reset to this common value. Could you eplain how this happens? – Łukasz Grabowski Feb 03 '15 at 21:57
  • No, I cannot. There's nothing in Python that would let what you described happen. – kindall Feb 03 '15 at 21:58
  • Kindall, I believe what you say is true, but I still don't understand. Would you care to have a look at my script? I posted the link above. – Łukasz Grabowski Feb 03 '15 at 22:14
  • I figured it out! :-) The point is that (**) also changes globalvisibility, but the changed value is used only on a particular instance in the rebuild() function, so it _looks_ as if all the instances were using separate visibility data. In short, I hacked myself :D – Łukasz Grabowski Feb 03 '15 at 23:33