• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 239
  • Last Modified:

Nested Lists after the 3rd level refer to the same id() / object OR cannot nest lists more than 2 levels deep

here is the code, basically it's a nesting issue, and I am doing something wrong, or not handling references properly, or something.

class Descriptor:      
      id               = None
      type             = None
      name             = None
      source           = None
      childDescriptors = []

if __name__ == "__main__":
        descriptors  = []
      currentDepth = None
      
      descriptor        = Descriptor()
      descriptor.id     = "id1"
      descriptor.source = "source1"
      descriptor.name   = "name1"
      
        descriptors.append(descriptor)
      
      #currentDepth should be a reference to the list stored in the descriptor object
        currentDepth =  descriptors[0].childDescriptors
      
      descriptor        = Descriptor()
      descriptor.id     = "id2"
      descriptor.source = "source2"
      descriptor.name   = "name2"
      
      print str(id(currentDepth)) + " : "+ str(currentDepth)
      # create a new Descriptor and append it to the childDescriptors list
        currentDepth.append(descriptor)
      
      # now set currentDepth to be a reference to the childDescriptors list within the Descriptor we
        # just appended
        currentDepth = descriptors[0].childDescriptors[0].childDescriptors
      print str(id(currentDepth)) + " : "+ str(currentDepth)

Now the output is this:
841768 : []
841768 : [<__main__.Descriptor instance at 0x877d8>]

the id's are the same and I don't know why.  I am just not understanding how the references are equilivant when I am specifically dropping down to another list at a different level.

Where have I gone wrong with my understanding of this?
0
aventure
Asked:
aventure
  • 7
  • 3
1 Solution
 
aventureAuthor Commented:
Oh, if it helps to know why I am doing this, the code above is just a sample that demonstrates my issues.  What I am doing is loading/parsing an XML document using SAX.  The "childDescriptors" are lists of the children with their properties.  Once the parsing is complete, then I recursively loop through all of the "nodes" in my list doing some code generation.
0
 
peprCommented:
Hi aventure. It seems that you have experience with other OOP language. In Python, you have to say explicitly self.something when refering to the instance member (data or method). In your case, you are using the member variables as class members (i.e. they belong to the class, not to the instance of the class). You are overwriting the content of the same class variable even though you wanted to assign the new value of the object (i.e. class instance) variable.
0
 
peprCommented:
Here is your corrected source. The comments follow below:

===================================================
class Descriptor:
    def __init__(self, xid=None, xtype=None, name=None, source=None):    
        self.id = xid
        self.type = xtype
        self.name = name
        self.source = source
        self.childDescriptors = []

if __name__ == "__main__":
    descriptors  = []
    currentDepth = None
   
    descriptor  = Descriptor()
    descriptor.id     = "id1"
    descriptor.source = "source1"
    descriptor.name   = "name1"
     
    descriptors.append(descriptor)
     
    #currentDepth should be a reference to the list stored in the descriptor object
    currentDepth =  descriptors[0].childDescriptors
     
    descriptor        = Descriptor()
    descriptor.id     = "id2"
    descriptor.source = "source2"
    descriptor.name   = "name2"
     
    print str(id(currentDepth)) + " : "+ str(currentDepth)
    # create a new Descriptor and append it to the childDescriptors list
    currentDepth.append(descriptor)
     
    # now set currentDepth to be a reference to the childDescriptors list within the Descriptor we
    # just appended
    currentDepth = descriptors[0].childDescriptors[0].childDescriptors
    print str(id(currentDepth)) + " : "+ str(currentDepth)
===================================================

In the class definition the __init__() method is called as the first one just after creation of the object. The first argument of each method is the reference to the object itself and it is usually named by convention as self.  When assigning members, you have to explicitly write self.memberId = something.

I have changed your code slightly, so that you can now use it the same way as you did, but also like this:

      descriptor  = Descriptor("id1", None, "name1", "source1")

0
How to Use the Help Bell

Need to boost the visibility of your question for solutions? Use the Experts Exchange Help Bell to confirm priority levels and contact subject-matter experts for question attention.  Check out this how-to article for more information.

 
peprCommented:
Also, the id and type are identifiers of the built-in functions. It is probably a good idea choose a different identifier for your variables.

You can also use one extra class Descriptor instance for expressing the root node of the whole structure (i.e. instead of your descriptors list). You can define your own append() method of the Descriptor class to do something special... and other things -- see below:

=================================================================
class Descriptor:
    def __init__(self, xid=None, xtype=None, name=None, source=None):    
        self.id = xid
        self.type = xtype
        self.name = name
        self.source = source
        self.childLst = []
       
    def append(self, descriptor):
        '''Appends the descriptor to the end of the child list.'''
        self.childLst.append(descriptor)
       
    def __getitem__(self, i):
        '''Implements indexing of the descriptor to get the content of the item of the child list.'''
        return self.childLst[i]
       
    def __str__(self):
        '''Returns the human readable representation of the object.'''
        return "%d : %s" % (id(self), str(self.childLst))


if __name__ == "__main__":
    rootDescriptor  = Descriptor("root")
    currentDescriptor = None
   
    descriptor  = Descriptor()
    descriptor.id     = "id1"
    descriptor.source = "source1"
    descriptor.name   = "name1"
     
    rootDescriptor.append(descriptor)
    currentDescriptor =  rootDescriptor[0]

    print currentDescriptor

    # create a new Descriptor and append it to the children list
    descriptor        = Descriptor()
    descriptor.id     = "id2"
    descriptor.source = "source2"
    descriptor.name   = "name2"
     
    currentDescriptor.append(descriptor)
     
    # now set currentDescriptor to be a reference to the Descriptor we
    # just appended
    currentDescriptor = rootDescriptor[0].childLst[0]
    print currentDescriptor
=================================================================

Here the abstraction of the root list of descriptors was replaced by one more root descriptor that contains the list. The __getitem__() method was defined to be able to access the child via its index -- each descriptor behaves as the list object if indexed. Because of this the last

    currentDescriptor = rootDescriptor[0].childLst[0]

could also be written as

    currentDescriptor = rootDescriptor[0][0]

The __str__() method is called internaly by str() built in function (also by the print command). This way debug prints can be written more easily.
0
 
peprCommented:
The question also is whether you really want to use SAX to build the document object model ;) You can use DOM directly -- implemented in Python http://docs.python.org/lib/module-xml.dom.html or other extenal library like ElementTree (is planned to became the part of standard distribution).
0
 
peprCommented:
Well the ElementTree IS already part of Python since version 2.5. See

8. Structured Markup Processing Tools  http://docs.python.org/lib/markup.html
8.13 xml.etree.ElementTree... http://docs.python.org/lib/module-xml.etree.ElementTree.html

Look around in the documentation. It probably implements the things that you want to implement.
0
 
aventureAuthor Commented:
Oh, I got it working properly with SAX, I choose sax just cause it's faster, and I have unknown elements that I would be adding too the document tree.  Basically the XML markup can change to be any number of things  based on user provided namespaces.  I am sure DOM can handle that, but I like the speed of SAX in this case.  Now I am not the foremost expert on DOM vs SAX.  Aside from speed, since I have it properly following the document tree right now, I figure why rewrite it.

etree seems pretty cool though.
0
 
aventureAuthor Commented:
pepr,

Thanks for the tutorial!  I did not catch onto that difference between class instance and instance variables in the code samples I was looking at.  Then again I just jumped right into with nary a nod to the docs.  My bad for not being more diligent.

So if class instance variables are shared across all instances of a class, that pretty much would mean, on a basic level at least, you could rid yourself of the Singleton pattern in python.
0
 
peprCommented:
A class combines both description of data structures and functionality with manipulation with the data structures -- the methods. From that point of view, class can be represented by the source code. It is a description. It has nothing to do with the future existence of that class -- except, it defines how the object will look like and how they will behave.

A class instance is often call an object. It has its existence. It is created, exists for some time and then it is destroyed. The object (i.e. the class instance) has some identification and (usually) some data owned by the object -- the member variables (or data items inside the object). On the other hand, its class could be here theoretically forever. When class is defined but not used, then typically there are no data elements (no reserved space in memory for storing some values).

A class variable--in some sense--is a stranger to the clean OOP design. Think about using a class variable as a counter of created instances, or similar functionality that is not directly required or used by the objects of the class. Class variables are often used for a kind of management. It is cleaner to create a real object responsible for management, of another dedicated class (posibly a singleton pattern can be used for creating the class instance, the manager).

Actually, Python is a hybrid language. It is probably easier to express a singleton via a module (via the module variables and module functions).
0
 
peprCommented:
Correction: The class has nothing to do with the future existence of that class instancec (i.e. the objects of that class)-- except, it defines how the objects will look like and how they will behave.

(My English may not be perfect sometimes. Feel free to ask for explanation. ;)

And thanks for the points.
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 7
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now