Lists as members are not unique?

On 21/03/2013 at 16:51, xxxxxxxx wrote:

During the process of porting a C++ plugin to Python I stumbled upon something REALLY wacky.
In C++ we can write a plugin that consists of a Gedialog plugin and a TagData plugin.
Then we can get the Tag plugin's class member variables from within the GeDialog code and use it.

Upon doing this same thing in Python. It seemed to mostly work the same way as C++...Except in the case of lists (arrays) for some strange reason.
If I add a list as a class member of the tag. Then change that list via the GeDialog plugin. When I make a new tag on another object. That list's values get carried over to that new tag!!?
In other words. The class member list in my tags are not unique ( per tag ) as they are supposed to be.

Other types of variables don't do this... Only lists.
Every time a new tag is created. Other types of class members are unique as expected. But not when it comes to lists.

Here's an example of a GeDialog plugin that sets and gets the class members on a tag plugin.

import c4d,sys,os  
from c4d import plugins, utils, bitmaps, gui, documents  
  
TAG_PLUGIN_ID = 1000008           # THIS id IS FOR TESTING PUPOSES ONLY!!!  
DIALOG_PLUGIN_ID =1000009         # THIS id IS FOR TESTING PUPOSES ONLY!!!   
  
  
################################################################  
###################### Tag Plugin ##############################  
  
class StorageTag(plugins.TagData) :  
  
  tagListData = ["Nothing"]            #A list that will be changed by the dialog plugin  
  tagVariableValue = "Nothing"        #A variable that will be changed by the dialog plugin  
  
  def Init(self, tag) :  
      data = tag.GetDataInstance()      
      return True  
   
  def Execute(self, tag, doc, op, bt, priority, flags) :       
      return True     
  
if __name__ == "__main__":  
 path, fn = os.path.split(__file__)  
 bmp = bitmaps.BaseBitmap()  
 bmp.InitWith(os.path.join(path, "res/icons/", "myicon.png"))     
 plugins.RegisterTagPlugin(TAG_PLUGIN_ID, "PyStorageTag",c4d.TAG_VISIBLE + c4d.TAG_EXPRESSION,StorageTag,"MyTag_dec", bmp)  
   
  
################################################################  
################### Dialog Plugin ##############################  
  
  
#enums  
BUTTON =  1001  
BUTTON2=  1002  
  
class MyDialog_Gui(gui.GeDialog) :  
   
  def CreateLayout(self) :    
      self.SetTitle("Dialog")       
      self.AddButton(BUTTON, c4d.BFH_CENTER, 60, 10, name="Press Me")  
      self.AddButton(BUTTON2, c4d.BFH_CENTER, 80, 10, name="Get Tag value")  
      return True  
    
  def InitValues(self) :   
      return True  
  
  def Command(self, id, msg) :  
    
      doc = documents.GetActiveDocument()  
  
      activeObj = doc.GetActiveObject()                     #The obect holding the tag  
      if not activeObj: return False  
      tag = activeObj.GetTag(TAG_PLUGIN_ID)                 #Get the tag on the object  
      if not tag: return False  
  
      st = tag.GetNodeData()                                #Get The tag's data (including the class members)              
  
      if id == BUTTON:              
          st.tagVariableValue    = "variable Value Changed"    #Change the value of the tag's member variable          
          st.tagListData[0] = "List Value Changed"          #Change the value of the tag's member list          
                
      if id == BUTTON2:  
          print st.tagVariableValue                         #Get the current values of the tag's members  
          print st.tagListData[0]              
  
      c4d.EventAdd()      
      return True  
  
    
class myDialog_Main(plugins.CommandData) :  
  dialog = None  
    
  def Execute(self, doc) :  
      if self.dialog is None:  
          self.dialog = MyDialog_Gui()  
      return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=DIALOG_PLUGIN_ID, defaultw=0, defaulth=0, xpos=-1, ypos=-1)  
        
  def RestoreLayout(self, sec_ref) :  
      if self.dialog is None:  
          self.dialog = MyDialog_Gui()  
      return self.dialog.Restore(pluginid=DIALOG_PLUGIN_ID, secret=sec_ref)   
  
if __name__ == "__main__":  
 path, fn = os.path.split(__file__)  
 bmp = bitmaps.BaseBitmap()  
 bmp.InitWith(os.path.join(path, "res/icons/", "None"))  
 plugins.RegisterCommandPlugin(DIALOG_PLUGIN_ID, "My Dialog",0,None,"", myDialog_Main())

This is really wacky.  And doesn't seem to happen in C++ plugins.
How do I stop lists from spilling over into other instances of the tag like this?
Is this a bug?

-ScottA

On 21/03/2013 at 18:58, xxxxxxxx wrote:

1. have you tried to reference to class instance explicitly on member allocation / use a constructor ?
at least for me this is working for a somehwat compareable case.

2. GetNodeData() is actually not listed as method for GeListNode in the python docs, so i am not
sure if we are supposed to use it or how far it does work.

On 21/03/2013 at 19:40, xxxxxxxx wrote:

This seems to fix the problem:

class StorageTag(plugins.TagData) :  
  
  def __init__(self, tagListData=[]) :  
      super(StorageTag, self).__init__()  
      self.tagListData = tagListData

It's strange that we are forced to use custom constructors to make lists work. But not other types of variables.

-ScottA

On 21/03/2013 at 21:02, xxxxxxxx wrote:

Argh!
Nope.. Still doesn't work. 😠

-ScottA

On 21/03/2013 at 21:43, xxxxxxxx wrote:

do not pass lists as optional parameters, it will cause all instances to share the 
same list if you skip the parameter on invoking the method. a possible workarround
is the following :

def __init__ (self, somelist = []) :
self.foo = somelist[:]

related thread

On 22/03/2013 at 00:41, xxxxxxxx wrote:

Hi Scott,

list objects are mutable. The list is created once at the place you wrote it. Every instance will have
a reference to the exactly same list when you create it on class-level or as a default-argument (since
those are created only once, too). One has to be very careful with mutable types within class
instances. No value in Python is passed by value, they're always passed by reference. But strings
and integers and others are immutable which creates the illusion of them being passed by
value.

Best,
-Niklas

On 22/03/2013 at 08:30, xxxxxxxx wrote:

Thanks Guys.
That's got it working. 👍

-ScottA