Setting Class Attributes

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • the.theorist

    #1

    Setting Class Attributes

    I have a small, simple class which contains a dictionary (and some
    other stuff, not shown). I then have a container class (Big) that holds
    some instances of the simple class. When I try to edit the elements of
    the dictionary, all instances obtain those changes; I want each
    instance to hold separate entries.

    #----------Begin module test.py
    class ex:
    def __init__(self, val={}):
    self.value = val
    def __str__(self):
    return str(self.value)

    class Big:
    def __init__(self):
    self.A = ex()
    self.B = ex()
    def __str__(self)
    return "A=%s, B=%s"%(self.A, self.B)
    def changea(self):
    a = self.A.value
    a['x'] = 1
    a['y'] = 10

    print "x = Big()"
    x = Big()
    print x

    print "x.changea( )"
    x.changea()
    print x

    print "x.B.value = 30"
    x.B.value = 30
    print x
    #----------End module test.py

    #----------Begin actual output
    x = Big()
    A={}, B={}
    x.changea()
    A={'x':1, 'y':10}, B={'x':1, 'y':10}
    x.B.value = 30
    A={'x':1, 'y':10}, B=30
    #----------End actual output

    #----------Begin Desired output
    x = Big()
    A={}, B={}
    x.changea()
    A={'x':1, 'y':10}, B={}
    x.B.value = 30
    A={'x':1, 'y':10}, B=30
    #----------End Desired output

    I'm never actually planning on changing the dictionary into something
    else, I just included that test to see wether both A and B would be set
    to 30. I'm clearly missing something here: Why do both dictionaries
    appear to get the same data when they are clearly separate instances?

  • Bruno Desthuilliers

    #2
    Re: Setting Class Attributes

    the.theorist a écrit :[color=blue]
    > I have a small, simple class which contains a dictionary (and some
    > other stuff, not shown). I then have a container class (Big) that holds
    > some instances of the simple class. When I try to edit the elements of
    > the dictionary, all instances obtain those changes; I want each
    > instance to hold separate entries.
    >
    > #----------Begin module test.py
    > class ex:[/color]

    class ex(object): # oldstyle classes are deprecated
    [color=blue]
    > def __init__(self, val={}):
    > self.value = val[/color]

    You didn't search very long. This is one of the most (in)famous Python
    gotchas: default args are evaluated *only once*, when the function
    definition is evaluated (at load time). This is also a dirty trick to
    have a 'static' (as in C) like variable.

    The solution is quite simple:
    class ex(object):
    def __init__(self, val=None):
    if val is None: val = {}
    self.value = val

    (snip)

    Comment

    • the.theorist

      #3
      Re: Setting Class Attributes


      Bruno Desthuilliers wrote:[color=blue]
      > the.theorist a écrit :[color=green]
      > > I have a small, simple class which contains a dictionary (and some
      > > other stuff, not shown). I then have a container class (Big) that holds
      > > some instances of the simple class. When I try to edit the elements of
      > > the dictionary, all instances obtain those changes; I want each
      > > instance to hold separate entries.
      > >
      > > #----------Begin module test.py
      > > class ex:[/color]
      >
      > class ex(object): # oldstyle classes are deprecated
      >[color=green]
      > > def __init__(self, val={}):
      > > self.value = val[/color]
      >
      > You didn't search very long. This is one of the most (in)famous Python
      > gotchas: default args are evaluated *only once*, when the function
      > definition is evaluated (at load time). This is also a dirty trick to
      > have a 'static' (as in C) like variable.
      >
      > The solution is quite simple:
      > class ex(object):
      > def __init__(self, val=None):
      > if val is None: val = {}
      > self.value = val
      >
      > (snip)[/color]

      Hey, that was extremely helpful. I suspect that the default args
      evaluation is optimized for speed. So it makes sense to use the None
      assignment, and a test condition later.

      Worked like a charm, Thanks!

      Comment

      • Bengt Richter

        #4
        Re: Setting Class Attributes

        On 25 Oct 2005 15:57:41 -0700, "the.theori st" <the.theorist@g mail.com> wrote:
        [color=blue]
        >
        >Bruno Desthuilliers wrote:[color=green]
        >> the.theorist a =E9crit :[color=darkred]
        >> > I have a small, simple class which contains a dictionary (and some
        >> > other stuff, not shown). I then have a container class (Big) that holds
        >> > some instances of the simple class. When I try to edit the elements of
        >> > the dictionary, all instances obtain those changes; I want each
        >> > instance to hold separate entries.
        >> >
        >> > #----------Begin module test.py
        >> > class ex:[/color]
        >>
        >> class ex(object): # oldstyle classes are deprecated
        >>[color=darkred]
        >> > def __init__(self, val=3D{}):
        >> > self.value =3D val[/color]
        >>
        >> You didn't search very long. This is one of the most (in)famous Python
        >> gotchas: default args are evaluated *only once*, when the function
        >> definition is evaluated (at load time). This is also a dirty trick to
        >> have a 'static' (as in C) like variable.
        >>
        >> The solution is quite simple:
        >> class ex(object):
        >> def __init__(self, val=3DNone):
        >> if val is None: val =3D {}
        >> self.value =3D val
        >>
        >> (snip)[/color]
        >
        >Hey, that was extremely helpful. I suspect that the default args
        >evaluation is optimized for speed. So it makes sense to use the None
        >assignment, and a test condition later.[/color]
        That sounds like you missed the important point: The reason for using None
        instead of {} as a default argument is that default arguments are only
        evaluated when the function is defined, not when it's called, so if the
        default value is {}, that very _same_ dict will be used as default for
        the next call to __init__, so you will have every instance of ex looking
        at the same val and binding it to its self.value, and then if one instance
        modifies it (which it can, since a dict is mutable), the other instances
        will see the modification in their self.value dicts. The None default
        prevents re-use of a dict that wasn't actually passed in as an argument
        replacing the default in the call. The call-time test for None discovers
        that a fresh dict is needed, and self.value = {} creates that fresh dict
        when __init__ executes, so the new instance gets its own separate self.value dict.

        Nothing to do with optimization. In fact, re-using the shared default dict
        would be faster, though of course generally wrong.[color=blue]
        >
        >Worked like a charm, Thanks!
        >[/color]
        Just wanted to make sure you realize why it made a difference, in case ;-)

        Regards,
        Bengt Richter

        Comment

        • the.theorist

          #5
          Re: Setting Class Attributes

          So that it'll be easier to remember the next time I find myself in the
          same situation on a different task, I'll extend the discussion
          somewhat.

          Coming from C, I had expected that I'd get a new empty dict every time
          the __init__ function ran. Guido (or some other benevolent) had decided
          to implement things a little bit differently in Python. I understand
          that most everything is a pointer in Python. (which gives us cool stuff
          like recursive data structures) So I was wondering, they could have
          made the behavior C-like, but chose not to. The decision to bind
          everything in the function to the same default args must be a
          reflection of the Python Ideology. Philosophically , why was it done
          this way, and how does it fit in with Python's design as a language.

          (hopefully, reasons will help me remeber why things are the way they
          are, so I don't forget in the future)
          -----------------
          I've only been using Python for a few weeks. (Chose it over Perl,
          because Python syntax is cleaner). I really like Python (over C), as it
          makes coding and debugging much faster and easier.

          Comment

          • Steve Holden

            #6
            Re: Setting Class Attributes

            the.theorist wrote:[color=blue]
            > So that it'll be easier to remember the next time I find myself in the
            > same situation on a different task, I'll extend the discussion
            > somewhat.
            >
            > Coming from C, I had expected that I'd get a new empty dict every time
            > the __init__ function ran. Guido (or some other benevolent) had decided
            > to implement things a little bit differently in Python. I understand
            > that most everything is a pointer in Python. (which gives us cool stuff
            > like recursive data structures) So I was wondering, they could have
            > made the behavior C-like, but chose not to. The decision to bind
            > everything in the function to the same default args must be a
            > reflection of the Python Ideology. Philosophically , why was it done
            > this way, and how does it fit in with Python's design as a language.
            >[/color]
            I couldn't claim to be channelling the developers, but I suspect that
            the decision was pragmatic.

            Consider what would have to be done in the case of a method prototype
            such as

            class myClass(object) :
            def __init__(arg1, arg2=default):
            print "Arg2 is:", arg2

            Remember as well that the "def" statement is executable: when executed
            it binds the function name to the compiled function body in the current
            namespace.

            Suppose the "default" name had gone out of scope by the time the
            instances are created: what value would you have the ionterpreter bind
            to the argument in that case?
            [color=blue]
            > (hopefully, reasons will help me remeber why things are the way they
            > are, so I don't forget in the future)
            > -----------------
            > I've only been using Python for a few weeks. (Chose it over Perl,
            > because Python syntax is cleaner). I really like Python (over C), as it
            > makes coding and debugging much faster and easier.
            >[/color]
            Yup, it's a great languae, and it seems to be increasing in popularity
            quite rapidly.

            regards
            Steve
            --
            Steve Holden +44 150 684 7255 +1 800 494 3119
            Holden Web LLC www.holdenweb.com
            PyCon TX 2006 www.python.org/pycon/

            Comment

            • Ron Adam

              #7
              Re: Setting Class Attributes



              the.theorist wrote:
              [color=blue]
              > So that it'll be easier to remember the next time I find myself in the
              > same situation on a different task, I'll extend the discussion
              > somewhat.
              >
              > Coming from C, I had expected that I'd get a new empty dict every time
              > the __init__ function ran. Guido (or some other benevolent) had decided
              > to implement things a little bit differently in Python. I understand
              > that most everything is a pointer in Python. (which gives us cool stuff
              > like recursive data structures) So I was wondering, they could have
              > made the behavior C-like, but chose not to. The decision to bind
              > everything in the function to the same default args must be a
              > reflection of the Python Ideology. Philosophically , why was it done
              > this way, and how does it fit in with Python's design as a language.[/color]

              I didn't read too closely the thread leading up to this point so I may
              be stating something already stated or that is already obvious to you.

              There is a difference between a pointer and name binding that you should
              keep in mind. When using pointers in C you can feasibly have a chain of
              pointers. In Python, it doesn't work that way unless you explicitly
              define a pointer type object to be used in such a way. The normal
              behavior is for an expression to bind a name with the object that is
              bound to the other name. So...

              a = 1 # a binds to 1
              b = a # b binds to 1
              c = b # c binds to 1

              So if you change what 'a' is bound to, 'b' and 'c' are still each bound
              to 1.

              Some of the places where you get pointer type behavior is in container
              objects. Look at the following sequence of instructions.

              a = 1 # a binds to 1
              b = [a] # first item in list is bound to 1,
              b binds to list.
              c = b # c bind to same list b is bound to.
              a = 2 # a binds to 2
              print b[0] -> 1 # print first item in list b is bound to.
              b[0] = 3 # change first item in list b is bound to to 3
              print c[0] -> 3 # print first item in list c is bound to,
              # (same list as b is bound to)

              If you can follow this easily you are well on your way to understanding
              Pythons storage model and philosophy. Notice, a name isn't bound to
              another name.

              This relationship is all over Python including the name binding of
              functions and class methods. And also in class's as well.

              Keep in mind the class body is executed at the time it is defined, and
              the methods are defined but not executed until they are used. In the
              case of the __init__ it is when the class is called after an instance
              object is created. When instances are created they share any class
              objects that were bound to names when the class was defined. They don't
              share any objects that get bound to instance attribute names in methods
              later.

              Does this help any?

              Cheers,
              Ron


              [color=blue]
              > (hopefully, reasons will help me remeber why things are the way they
              > are, so I don't forget in the future)
              > -----------------
              > I've only been using Python for a few weeks. (Chose it over Perl,
              > because Python syntax is cleaner). I really like Python (over C), as it
              > makes coding and debugging much faster and easier.
              >[/color]

              Comment

              Working...