Class function holds variable after execution

This is the place for queries that don't fit in any of the other categories.

Class function holds variable after execution

Postby austinmilt » Wed Sep 11, 2013 8:52 pm

I am not really sure the words to use to describe this problem. The gist is this: I have created a custom class, "Vert1", each instance of which is an element in a linked list. In addition, there's another class, "Point", which is supposed to form the head of the linked list. You can think of the system as a Ball ("Point") and series of vertices ("Vert1") connected by a string to the Ball.

My way of automatically updating object's previous and next neighbors in the list has created some weird problem. I created a set() method that sets attributes of Vert1 and recursively updates its neighbors based on certain changes. Vert1.set() is passed, optionally, a dictionary and **kwargs. When Vert1.set() is called within another call of Vert1.set() and no dictionary is passed , the second called Vert1.set() has a dictionary with the original keys and values used to define that Vert1. The expected behavior is that the second called Vert1.set() should have an empty dictionary (as defined by "def set(self, uParams={}, **kwargs).

That all seems very confusing to me. In an effort to illustrate, I've put a code example below

What you're looking for when you run this is that last printout of "uParams: {'next': <Vert1 (3, 3)>}", which should be "uParams: {}" if it was working as expected. If you look more into the code, you can see that if this were to continue, it would cause an infinite loop, which is the reason for the Exception.

Finally, I can avoid this problem by making all calls to Vert1.set() include a uParams definition. Usually this would be Vert1.set(uParams={}).

Any help clarifying why this is a feature would be great! Let me know what additional information is needed.


Code: Select all
#!/usr/bin/python

# \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ #
# Point ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| #
# /////////////////////////////////////////////////////////////////////////// #
class Point:

    def __init__(self, uParams={}, **kwargs):
   
        # update parameters
        params = {
            'vert1': None,
            'vert2': None,
            'id': None,
            'i': None,
            'j': None,
        }
       
        params.update(uParams)
        params.update(kwargs)
       
        # assign attributes
        for k in params: setattr(self, k, None)
        self.set(params)
       
    def __repr__(self): return '<Point (%s, %s)>' % (self.i.__str__(), self.j.__str__())
   
    def __str__(self): return 'W'
   
    def set(self, uParams={}, **kwargs):
   
        print 'uParams:',  uParams
       
        uParams.update(kwargs)
       
        for k in uParams:
           
            # # # Update Other Infrastructure Before Changing Self # # #
            if uParams[k].__class__.__name__ in ('Vert2', 'Vert1'):
               
                if k == 'vert1':
               
                    # update the current 'vert1'
                    if self.vert1 is not None:
                        uParams[k].set(point=None)
                       
                elif k == 'vert2':
                   
                    # update the current 'vert2'
                    if self.vert2 is not None:
                        uParams[k].set(point=None)
                       
               
            # # # Update Self # # #
            # set this variable's attribute
            setattr(self, k, uParams[k])
               
               
            # # # Update Other Infrastructure After Changing Self # # #
            if uParams[k].__class__.__name__ in ('Vert2', 'Vert1'):
                if k in ('vert1','vert2'):
               
                    # set the infrastructure's point to this
                    uParams[k].set(point=self)
           
       
# \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ #
# Vert1 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| #
# /////////////////////////////////////////////////////////////////////////// #
class Vert1:
    def __init__(self, uParams={}, **kwargs):
   
        # update parameters
        params = {
            'point': None,
            'i': None,
            'j': None,
            'prev': None,
            'next': None
        }
       
        params.update(uParams)
        params.update(kwargs)
           
        # assign attributes
        for k in params: setattr(self, k, None)
        self.set(params)

    def __repr__(self): return '<Vert1 (%s, %s)>' % (self.i.__str__(), self.j.__str__())
   
    def __str__(self): return 'R'
   
    def set(self, uParams={}, **kwargs):
   
        print 'uParams:',  uParams
       
        uParams.update(kwargs)
       
        for k in uParams:
           
            # check that previous and next are not self, which can cause
            # infinite recursion
            if (k in ('prev', 'next')) and (uParams[k] is self):
                raise ValueError('Cannot define [prev] or [next] as self.')
               
            # # # Update Other Infrastructure Before Changing Self # # #
            if uParams[k].__class__.__name__ in ('Point', 'Vert1'):
               
                # set the previous
                if k == 'prev':
               
                    # update the current previous' next or 'vert1'
                    if self.prev is not None:
                        if self.prev.__class__.__name__ == 'Point':
                            print 'setting [point]\'s [vert1] to None'
                            self.prev.set(vert1=None)
                        else:
                            print 'setting [prev]\'s [next] to None'
                            self.prev.set(next=None)
                       
               
            # # # Update Self # # #
            # set this variable's attribute
            setattr(self, k, uParams[k])
               
               
            # # # Update Other Infrastructure After Changing Self # # #
            if uParams[k].__class__.__name__ in ('Point', 'Vert1'):
           
                if k == 'prev':
               
                    # special for when the previous is a point
                    if uParams[k].__class__.__name__ == 'Point':
                   
                        self.set(point=uParams[k])
                       
                        # update other vert1s' points
                        if self.next is not None:
                            print 'setting [next]\'s [point] to [point]'
                            self.next.set(point=uParams[k])
                           
                        # set this point's vert1 if it doesnt have one
                        if uParams[k].vert1 is None:
                            print 'setting [point]\'s [vert1] to [self]'
                            uParams[k].set(vert1=self)
                           
                    else:
                   
                        # set the previous' next to this
                        if uParams[k].next is not self:
                            print 'setting [prev]\'s [next] to [self]'
                            uParams[k].set(next=self)
               
                # set the next
                elif k == 'next':
               
                    # set next's prevoius to self
                    if uParams[k].prev is not self:
                        print 'setting [next]\'s [prev] to [self]'
                        uParams[k].set(prev=self)
                       
                    # set next's point to self's point
                    print 'setting [next]\'s [point] to [point]'
                    uParams[k].set(point=self.point)
                       
                   
                   
# \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ #
# PROBLEM ILLUSTRATION |||||||||||||||||||||||||||||||||||||||||||||||||||||| #
# /////////////////////////////////////////////////////////////////////////// #
def main():

    print '\nDefine only [point]: A = Point(i=1,j=1,id=0)'
    A = Point(i=1,j=1,id=0)
   
    print '\nDefine first [vert1]: B = Vert1(i=2,j=2,point=A)'
    B = Vert1(i=2,j=2,point=A)
   
    print '\nDefine second [vert1]: C = Vert1(i=3,j=3,prev=B)'
    C = Vert1(i=3,j=3,prev=B)


if __name__ == '__main__':
    main()
austinmilt
 
Posts: 6
Joined: Tue Jun 25, 2013 1:07 pm

Re: Class function holds variable after execution

Postby micseydel » Wed Sep 11, 2013 9:43 pm

I haven't looked very closely at the yet, but I suspect your problem and solution can be exemplified by this similar code
Code: Select all
>>> def f(x=[]):
   x.append(1)
   print x

   
>>> f()
[1]
>>> f()
[1, 1]
>>> f()
[1, 1, 1]
>>> f()
[1, 1, 1, 1]
>>>
>>> def f(x=None):
   if x is None:
      x = [1]
   print x

   
>>> f()
[1]
>>> f()
[1]
>>> f()
[1]

If this doesn't help, feel free to post back and I'll take a closer look when I can.
Join the #python-forum IRC channel on irc.freenode.net!

Please do not PM members regarding questions which are meant to be discussed publicly. The point of the forum is so that others can benefit from it. We don't want to help you over PMs or emails.
User avatar
micseydel
 
Posts: 1228
Joined: Tue Feb 12, 2013 2:18 am
Location: Mountain View, CA

Re: Class function holds variable after execution

Postby austinmilt » Thu Sep 12, 2013 12:38 pm

micseydel wrote:I haven't looked very closely at the yet, but I suspect your problem and solution can be exemplified by this similar code...
If this doesn't help, feel free to post back and I'll take a closer look when I can.


That seems to have worked, but I do no understand why. It seems to me a function should only hold local variables until the execution completes. What is the feature I'm missing?
austinmilt
 
Posts: 6
Joined: Tue Jun 25, 2013 1:07 pm

Re: Class function holds variable after execution

Postby DrakeMagi » Thu Sep 12, 2013 1:44 pm

When dict or list use in function or methods. They don't forget.
micseydel showing how this {} is holding the info.
It not a new empty dict everytime. It the same one.
sample output would help.

here another option
Code: Select all
class Point(object):
   def __init__(self, params=None, **kargs):
      print(self.__dict__)
      
      self.vert1 = None
      self.vert2 = None
      self.id_ = None
      self.i = None
      self.j = None
      
      print(self.__dict__)
      
      #if params is not None
      if params:
         self.__dict__.update(params)
         
      self.__dict__.update(kargs)
      print(self.__dict__)

a = Point(i=5)
Linux: won't find windows here.
Linux: the choice of a GNU generation.
https://github.com/DrakeMagi
DrakeMagi
 
Posts: 96
Joined: Sun May 12, 2013 8:36 pm

Re: Class function holds variable after execution

Postby austinmilt » Thu Sep 12, 2013 1:56 pm

I see that is the case. I am more interested to know why.
austinmilt
 
Posts: 6
Joined: Tue Jun 25, 2013 1:07 pm

Re: Class function holds variable after execution

Postby Mekire » Thu Sep 12, 2013 2:11 pm

User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Class function holds variable after execution

Postby austinmilt » Thu Sep 12, 2013 2:25 pm

Thank you all for your replies. I see how to fix the problem.

However, neither of your explanations actually say why Python is like this. I don't want to know how this feature of Python works, I want to know why this is a feature.

For instance, you might say: "Default objects are evaluated only once because object creation in Python is inefficient and this avoids (or reduces) that inefficiency," or you could say "Default objects are evaluated only once because, often, you might want to re-use values in that object on successive calls to the function."

I dont know if either of those is true, and I'm not sure it makes sense even if they are. But I hope that illustrates what kind of explanation I am hoping to get.
austinmilt
 
Posts: 6
Joined: Tue Jun 25, 2013 1:07 pm

Re: Class function holds variable after execution

Postby Mekire » Thu Sep 12, 2013 2:48 pm

Well try this on for size and see if you like the explanation you get.

http://stackoverflow.com/a/1145781

If you aren't satisfied with the content you find there though, you might have to ask Guido :P

-Mek
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Class function holds variable after execution

Postby austinmilt » Thu Sep 12, 2013 2:57 pm

Excellent! And now I see why it makes sense.

The part I was missing is that (pretty much?) everything in Python is an object - which I knew but didnt apply here - and so those class methods are only created once. Not sure that is correct, but it makes sense in my head now.

Thanks!
austinmilt
 
Posts: 6
Joined: Tue Jun 25, 2013 1:07 pm


Return to General Coding Help

Who is online

Users browsing this forum: Baidu [Spider] and 4 guests