Assigning to self

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Frans Englich

    #1

    Assigning to self


    Hello,

    I am having trouble with throwing class instances around. Perhaps I'm
    approaching my goals with the wrong solution, but here's nevertheless a
    stripped down example which demonstrates my scenario:

    #------------------------------------------------------------------------------------------

    class foo:
    tests = {}
    def __init__( self, id ):

    try:
    me = self.__class__. tests[ id ]

    except KeyError:
    print "Did not exist, initializing myself.."
    self.attr = "exists"
    self.__class__. tests[ id ] = self

    else:
    print "Already exists! Re-using existing instance"
    self = me

    print "Me", self.attr + "!" # line 18

    def yo(self):
    return self.attr # line 21

    def main():

    a = foo( "test" )
    print "ATTR:", a.yo()

    b = foo( "test" )
    print "ATTR:", b.yo()

    if __name__ == "__main__":
    main()

    #------------------------------------------------------------------------------------------

    This is the output:

    Did not exist, initializing myself..
    Me exists!
    ATTR: exists
    Already exists! Re-using existing instance
    Me exists!
    ATTR:
    Traceback (most recent call last):
    File "cpClass.py ", line 32, in ?
    main()
    File "cpClass.py ", line 29, in main
    print "ATTR:", b.yo()
    File "cpClass.py ", line 21, in yo
    return self.attr # line 21
    AttributeError: foo instance has no attribute 'attr'
    #------------------------------------------------------------------------------------------

    What the code attempts to do is implementing a, to the API user, transparent
    memory-saver by ensuring that no more than one instance of the class foo
    exists for a particular id. E.g, the user can simply "create" an instance and
    if one not already exists, it is created.

    First of all; am I approaching the goal with the right solution?

    The way I do fails, obviously. The line 'self = me'(scary..) doesn't really
    work for the attribute attr; the attribute exists on line 21, but it fails
    when yo() tries to access it. What have failed? Is it a namespace scope
    issue? Do 'self = me' do what I think it should?


    Cheers,

    Frans




  • Peter Otten

    #2
    Re: Assigning to self

    Frans Englich wrote:
    [color=blue]
    > What the code attempts to do is implementing a, to the API user,
    > transparent memory-saver by ensuring that no more than one instance of the
    > class foo exists for a particular id. E.g, the user can simply "create" an
    > instance and if one not already exists, it is created.[/color]

    By the time __init__() is called, a new Foo instance has already been
    created. Therefore you need to implement Foo.__new__(). E. g.:
    [color=blue][color=green][color=darkred]
    >>> class Foo(object):[/color][/color][/color]
    .... cache = {}
    .... def __new__(cls, id):
    .... try:
    .... return cls.cache[id]
    .... except KeyError:
    .... pass
    .... cls.cache[id] = result = object.__new__( cls, id)
    .... return result
    .... def __init__(self, id):
    .... self.id = id
    .... def __repr__(self):
    .... return "Foo(id=%r) " % self.id
    ....[color=blue][color=green][color=darkred]
    >>> foos = map(Foo, "abca")
    >>> foos[/color][/color][/color]
    [Foo(id='a'), Foo(id='b'), Foo(id='c'), Foo(id='a')][color=blue][color=green][color=darkred]
    >>> foos[0] is foos[-1][/color][/color][/color]
    True[color=blue][color=green][color=darkred]
    >>> Foo.cache[/color][/color][/color]
    {'a': Foo(id='a'), 'c': Foo(id='c'), 'b': Foo(id='b')}

    Note that putting the instances into the cache prevents them from being
    garbage collected -- you may even end up with higher memory usage.
    Use a weakref.WeakVal ueDictionary instead of the normal dict to fix that.

    Peter


    Comment

    • Reinhold Birkenfeld

      #3
      Re: Assigning to self

      Frans Englich wrote:[color=blue]
      > Hello,
      >
      > I am having trouble with throwing class instances around. Perhaps I'm
      > approaching my goals with the wrong solution, but here's nevertheless a
      > stripped down example which demonstrates my scenario:
      >
      > #------------------------------------------------------------------------------------------
      >
      > class foo:
      > tests = {}
      > def __init__( self, id ):
      >
      > try:
      > me = self.__class__. tests[ id ]
      >
      > except KeyError:
      > print "Did not exist, initializing myself.."
      > self.attr = "exists"
      > self.__class__. tests[ id ] = self
      >
      > else:
      > print "Already exists! Re-using existing instance"
      > self = me
      >
      > print "Me", self.attr + "!" # line 18
      >
      > def yo(self):
      > return self.attr # line 21[/color]

      As 'self' is a method parameter, changing it only affects the current
      function. When __init__ is called, the instance is already created, so
      you can't change it.

      What you are looking for is a class factory (is this term correct?),
      here is a sample implementation (using 2.4 decorators):

      class foo:
      tests = {}
      @classmethod
      def get_test(cls, id):
      if cls.tests.has_k ey(id):
      return cls.tests[id]
      else:
      inst = cls()
      inst.attr = "exists"
      cls.tests[id] = inst
      return inst

      def yo(self):
      return self.attr

      Here you define get_test as a classmethod, that is, it does not receive
      the instance as first argument, but the class. It can be called from the
      class (foo.get_test) or an instance (foo().get_test ).

      An alternative might be to override __new__, but I'm sure someone other
      will suggest this.

      Reinhold

      Comment

      • John Roth

        #4
        Re: Assigning to self

        "Frans Englich" <frans.englich@ telia.com> wrote in message
        news:mailman.80 5.1105987092.22 381.python-list@python.org ...[color=blue]
        >
        > Hello,
        >[/color]
        [...]
        [color=blue]
        >
        > What the code attempts to do is implementing a, to the API user,
        > transparent
        > memory-saver by ensuring that no more than one instance of the class foo
        > exists for a particular id. E.g, the user can simply "create" an instance
        > and
        > if one not already exists, it is created.[/color]

        In other words, you're trying to create a singleton. In general,
        singletons are frowned on these days for a number of reasons,
        not least because of the difficulty of testing them.
        [color=blue]
        > First of all; am I approaching the goal with the right solution?[/color]

        No. In all Python releases since 2.2, the correct way of doing this is to
        use the __new__() method. Unfortunately, the only place it is documented
        is here:



        and here:



        The first reference contains an example of how to do
        a singleton: simply search on the word Singleton.

        John Roth
        [color=blue]
        > Cheers,
        >
        > Frans
        >
        >
        >
        >[/color]

        Comment

        • Frans Englich

          #5
          Re: Assigning to self

          On Monday 17 January 2005 19:02, Peter Otten wrote:[color=blue]
          > Frans Englich wrote:[color=green]
          > > What the code attempts to do is implementing a, to the API user,
          > > transparent memory-saver by ensuring that no more than one instance of
          > > the class foo exists for a particular id. E.g, the user can simply
          > > "create" an instance and if one not already exists, it is created.[/color]
          >
          > By the time __init__() is called, a new Foo instance has already been
          >
          > created. Therefore you need to implement Foo.__new__(). E. g.:[color=green][color=darkred]
          > >>> class Foo(object):[/color][/color]
          >
          > ... cache = {}
          > ... def __new__(cls, id):
          > ... try:
          > ... return cls.cache[id]
          > ... except KeyError:
          > ... pass
          > ... cls.cache[id] = result = object.__new__( cls, id)
          > ... return result
          > ... def __init__(self, id):
          > ... self.id = id
          > ... def __repr__(self):
          > ... return "Foo(id=%r) " % self.id
          > ...[/color]

          I'm not sure, but I think this code misses one thing: that __init__ is called
          each time __new__ returns it, as per the docs Peter posted.


          Cheers,

          Frans

          Comment

          • Frans Englich

            #6
            Re: Assigning to self

            On Monday 17 January 2005 20:55, Frans Englich wrote:[color=blue]
            > On Monday 17 January 2005 19:02, Peter Otten wrote:[color=green]
            > > Frans Englich wrote:[color=darkred]
            > > > What the code attempts to do is implementing a, to the API user,
            > > > transparent memory-saver by ensuring that no more than one instance of
            > > > the class foo exists for a particular id. E.g, the user can simply
            > > > "create" an instance and if one not already exists, it is created.[/color]
            > >
            > > By the time __init__() is called, a new Foo instance has already been
            > >
            > > created. Therefore you need to implement Foo.__new__(). E. g.:[color=darkred]
            > > >>> class Foo(object):[/color]
            > >
            > > ... cache = {}
            > > ... def __new__(cls, id):
            > > ... try:
            > > ... return cls.cache[id]
            > > ... except KeyError:
            > > ... pass
            > > ... cls.cache[id] = result = object.__new__( cls, id)
            > > ... return result
            > > ... def __init__(self, id):
            > > ... self.id = id
            > > ... def __repr__(self):
            > > ... return "Foo(id=%r) " % self.id
            > > ...[/color]
            >
            > I'm not sure, but I think this code misses one thing: that __init__ is
            > called each time __new__ returns it, as per the docs Peter posted.[/color]

            Ahem, John I ment :)

            The second typo today..


            Cheers,

            Frans

            Comment

            • Frans Englich

              #7
              Re: Assigning to self

              On Monday 17 January 2005 20:03, John Roth wrote:[color=blue]
              > "Frans Englich" <frans.englich@ telia.com> wrote in message[/color]

              <snip>
              [color=blue]
              > In other words, you're trying to create a singleton. In general,
              > singletons are frowned on these days for a number of reasons,
              > not least because of the difficulty of testing them.[/color]

              Then I have some vague, general questions which perhaps someone can reason
              from: what is then the preferred methods for solving problems which requires
              Singletons? Is it only frowned upon in Python code?


              Cheers,

              Frans

              Comment

              • Peter Otten

                #8
                Re: Assigning to self

                Frans Englich wrote:
                [color=blue][color=green][color=darkred]
                >> > >>> class Foo(object):
                >> >
                >> > ... cache = {}
                >> > ... def __new__(cls, id):
                >> > ... try:
                >> > ... return cls.cache[id]
                >> > ... except KeyError:
                >> > ... pass
                >> > ... cls.cache[id] = result = object.__new__( cls, id)
                >> > ... return result
                >> > ... def __init__(self, id):
                >> > ... self.id = id
                >> > ... def __repr__(self):
                >> > ... return "Foo(id=%r) " % self.id
                >> > ...[/color]
                >>
                >> I'm not sure, but I think this code misses one thing: that __init__ is
                >> called each time __new__ returns it, as per the docs Peter posted.[/color]
                >
                > Ahem, John I ment :)[/color]

                You are right -- just put the initialization into the __new__() method,
                then.

                Peter

                Comment

                • Peter Otten

                  #9
                  Re: Assigning to self

                  Frans Englich wrote:
                  [color=blue]
                  > On Monday 17 January 2005 20:03, John Roth wrote:[color=green]
                  >> "Frans Englich" <frans.englich@ telia.com> wrote in message[/color]
                  >
                  > <snip>
                  >[color=green]
                  >> In other words, you're trying to create a singleton. In general,
                  >> singletons are frowned on these days for a number of reasons,
                  >> not least because of the difficulty of testing them.[/color]
                  >
                  > Then I have some vague, general questions which perhaps someone can reason
                  > from: what is then the preferred methods for solving problems which
                  > requires Singletons? Is it only frowned upon in Python code?[/color]

                  Sorry, no answer here, but do you really want a singleton?

                  Singleton: "Ensure a class only has one instance, and provide a global point
                  of access to it"

                  whereas

                  Flyweight: "Use sharing to support large numbers of fine-grained objects
                  efficiently"

                  as per "Design Patterns" by Gamma et al.

                  Peter

                  Comment

                  • Frans Englich

                    #10
                    Re: Assigning to self

                    On Monday 17 January 2005 21:24, Peter Otten wrote:[color=blue]
                    > Frans Englich wrote:[color=green]
                    > > On Monday 17 January 2005 20:03, John Roth wrote:[color=darkred]
                    > >> "Frans Englich" <frans.englich@ telia.com> wrote in message[/color]
                    > >
                    > > <snip>
                    > >[color=darkred]
                    > >> In other words, you're trying to create a singleton. In general,
                    > >> singletons are frowned on these days for a number of reasons,
                    > >> not least because of the difficulty of testing them.[/color]
                    > >
                    > > Then I have some vague, general questions which perhaps someone can
                    > > reason from: what is then the preferred methods for solving problems
                    > > which requires Singletons? Is it only frowned upon in Python code?[/color]
                    >
                    > Sorry, no answer here, but do you really want a singleton?
                    >
                    > Singleton: "Ensure a class only has one instance, and provide a global
                    > point of access to it"
                    >
                    > whereas
                    >
                    > Flyweight: "Use sharing to support large numbers of fine-grained objects
                    > efficiently"
                    >
                    > as per "Design Patterns" by Gamma et al.[/color]

                    Hehe :) Singleton sounds like what I want, but OTOH I do not know what
                    Flyweight is, except for sounding interesting. Darn, I really must save for
                    that Design Patterns by GOF.


                    Cheers,

                    Frans

                    Comment

                    • John Roth

                      #11
                      Re: Assigning to self


                      "Frans Englich" <frans.englich@ telia.com> wrote in message
                      news:mailman.81 6.1105995336.22 381.python-list@python.org ...[color=blue]
                      > On Monday 17 January 2005 20:03, John Roth wrote:[color=green]
                      >> "Frans Englich" <frans.englich@ telia.com> wrote in message[/color]
                      >
                      > <snip>
                      >[color=green]
                      >> In other words, you're trying to create a singleton. In general,
                      >> singletons are frowned on these days for a number of reasons,
                      >> not least because of the difficulty of testing them.[/color]
                      >
                      > Then I have some vague, general questions which perhaps someone can reason
                      > from: what is then the preferred methods for solving problems which
                      > requires
                      > Singletons? Is it only frowned upon in Python code?[/color]

                      I don't know of any generic methods that will work in all
                      cases _and_ are easy to apply. There are really two issues:
                      testing and abuse of the pattern.

                      There really are some places where you want a single instance
                      of something that is globally visible. Things that leap immediately
                      to mind include the connection to a data base and a logger
                      facility.

                      However, in a lot of cases, you find singletons used as an
                      "object oriented" substitute for a global variable, which stinks
                      just as badly as a global would. Since Python is a multi-paradigm
                      language, just put in a global at the module level. It's clearer.
                      Otherwise redesign.

                      There are several ways of getting around the testing
                      difficulty. One is the "toolbox" pattern that appears in a
                      number of places. The idea there is to have a single
                      object that proxies all of the singletons so that they
                      can be replace by testing versions at initialization time.

                      A neater and more modern method is to use an IOC
                      (Inversion of Control) container, or at least the pattern
                      involved. All IOC containers I'm aware of support
                      singletons and make testing them almost trivially
                      easy.

                      A third way is to simply take advantage of Python's
                      dynamic nature and have your tests stuff the testing
                      instance into the class object before the first call. This
                      is an ad-hoc variation on the IOC principle.

                      HTH

                      John Roth
                      [color=blue]
                      >
                      >
                      > Cheers,
                      >
                      > Frans
                      >[/color]

                      Comment

                      • Marc 'BlackJack' Rintsch

                        #12
                        Re: Assigning to self

                        In <mailman.816.11 05995336.22381. python-list@python.org >, Frans Englich
                        wrote:
                        [color=blue]
                        > Then I have some vague, general questions which perhaps someone can reason
                        > from: what is then the preferred methods for solving problems which requires
                        > Singletons?[/color]

                        As already mentioned it's similar to a global variable. If I need a
                        "Singleton" I just put it as global into a module. Either initialize it
                        at module level or define a function with the content of your __init__().

                        Ciao,
                        Marc 'BlackJack' Rintsch

                        Comment

                        • Jeff Shannon

                          #13
                          Re: Assigning to self

                          Marc 'BlackJack' Rintsch wrote:
                          [color=blue]
                          > Frans Englich wrote:
                          >[color=green]
                          >>Then I have some vague, general questions which perhaps someone can reason
                          >>from: what is then the preferred methods for solving problems which requires
                          >>Singletons?[/color]
                          >
                          > As already mentioned it's similar to a global variable. If I need a
                          > "Singleton" I just put it as global into a module. Either initialize it
                          > at module level or define a function with the content of your __init__().[/color]

                          If one is determined to both use a Singleton and avoid having a plain
                          module-global variable, one could (ab)use function default parameters:

                          class __Foo:
                          "I am a singleton!"
                          pass

                          def Foo(foo_obj = __Foo()):
                          assert isinstance(foo_ obj, __Foo
                          return foo_obj

                          Of course, this suffers from the weakness that one might pass an
                          object as an argument to the factory function and thus temporarily
                          override the Singleton-ness of __Foo... but a determined programmer
                          will work around any sort of protection scheme anyhow. ;)

                          In general, ISTM that if one wants a Singleton, it's best to create it
                          via a factory function (even if that function then masquerades as a
                          class). This gives you pretty good control over the circumstances in
                          which your Singleton will be created and/or retrieved, and it also
                          makes it trivial to replace the Singleton with some other pattern
                          (such as, e.g., a Flyweight or Borg object) should the need to
                          refactor arise.

                          Jeff Shannon
                          Technician/Programmer
                          Credit International


                          Comment

                          • top

                            #14
                            Re: Assigning to self

                            Jeff Shannon wrote:[color=blue]
                            > class __Foo:
                            > "I am a singleton!"
                            > pass
                            >
                            > def Foo(foo_obj = __Foo()):
                            > assert isinstance(foo_ obj, __Foo
                            > return foo_obj[/color]
                            this is a bit simpler, I think, and takes advantage from Python's free
                            name-rebinding.

                            class Singleton(objec t):
                            def __call__(self):
                            return self
                            Singleton = Singleton()

                            And, now, every time you say obj = Singleton(), you're getting the same
                            instance. Of course, this corrupt the __call__ method of the class, and
                            isn't inheritance-safe at all, but if you want only one or two classes
                            like this, it isn't very much work to implement it. Also, it's free of
                            weird metaclasses, __new__s, or __dict__ hacks, I think.
                            Just putting my two-cents into this,
                            --
                            - Alexandre

                            Comment

                            Working...