Assigning to self

Discussion in 'Python' started by Frans Englich, Jan 17, 2005.

  1. 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
     
    Frans Englich, Jan 17, 2005
    #1
    1. Advertising

  2. Frans Englich

    Peter Otten Guest

    Frans Englich wrote:

    > 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.


    By the time __init__() is called, a new Foo instance has already been
    created. Therefore you need to implement Foo.__new__(). E. g.:

    >>> 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
    ....
    >>> foos = map(Foo, "abca")
    >>> foos

    [Foo(id='a'), Foo(id='b'), Foo(id='c'), Foo(id='a')]
    >>> foos[0] is foos[-1]

    True
    >>> Foo.cache

    {'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.WeakValueDictionary instead of the normal dict to fix that.

    Peter
     
    Peter Otten, Jan 17, 2005
    #2
    1. Advertising

  3. Frans Englich wrote:
    > 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


    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_key(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
     
    Reinhold Birkenfeld, Jan 17, 2005
    #3
  4. Frans Englich

    John Roth Guest

    "Frans Englich" <> wrote in message
    news:...
    >
    > Hello,
    >

    [...]

    >
    > 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.


    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.

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


    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:

    http://www.python.org/2.2.3/descrintro.html

    and here:

    http://users.rcn.com/python/download/Descriptor.htm

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

    John Roth

    > Cheers,
    >
    > Frans
    >
    >
    >
    >
     
    John Roth, Jan 17, 2005
    #4
  5. On Monday 17 January 2005 19:02, Peter Otten wrote:
    > Frans Englich wrote:
    > > 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.

    >
    > By the time __init__() is called, a new Foo instance has already been
    >
    > created. Therefore you need to implement Foo.__new__(). E. g.:
    > >>> 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
    > ...


    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
     
    Frans Englich, Jan 17, 2005
    #5
  6. On Monday 17 January 2005 20:55, Frans Englich wrote:
    > On Monday 17 January 2005 19:02, Peter Otten wrote:
    > > Frans Englich wrote:
    > > > 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.

    > >
    > > By the time __init__() is called, a new Foo instance has already been
    > >
    > > created. Therefore you need to implement Foo.__new__(). E. g.:
    > > >>> 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
    > > ...

    >
    > 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.


    Ahem, John I ment :)

    The second typo today..


    Cheers,

    Frans
     
    Frans Englich, Jan 17, 2005
    #6
  7. On Monday 17 January 2005 20:03, John Roth wrote:
    > "Frans Englich" <> wrote in message


    <snip>

    > 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.


    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
     
    Frans Englich, Jan 17, 2005
    #7
  8. Frans Englich

    Peter Otten Guest

    Frans Englich wrote:

    >> > >>> 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
    >> > ...

    >>
    >> 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.

    >
    > Ahem, John I ment :)


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

    Peter
     
    Peter Otten, Jan 17, 2005
    #8
  9. Frans Englich

    Peter Otten Guest

    Frans Englich wrote:

    > On Monday 17 January 2005 20:03, John Roth wrote:
    >> "Frans Englich" <> wrote in message

    >
    > <snip>
    >
    >> 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.

    >
    > 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?


    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
     
    Peter Otten, Jan 17, 2005
    #9
  10. On Monday 17 January 2005 21:24, Peter Otten wrote:
    > Frans Englich wrote:
    > > On Monday 17 January 2005 20:03, John Roth wrote:
    > >> "Frans Englich" <> wrote in message

    > >
    > > <snip>
    > >
    > >> 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.

    > >
    > > 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?

    >
    > 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.


    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
     
    Frans Englich, Jan 17, 2005
    #10
  11. Frans Englich

    John Roth Guest

    "Frans Englich" <> wrote in message
    news:...
    > On Monday 17 January 2005 20:03, John Roth wrote:
    >> "Frans Englich" <> wrote in message

    >
    > <snip>
    >
    >> 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.

    >
    > 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?


    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

    >
    >
    > Cheers,
    >
    > Frans
    >
     
    John Roth, Jan 17, 2005
    #11
  12. In <>, Frans Englich
    wrote:

    > 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?


    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
     
    Marc 'BlackJack' Rintsch, Jan 18, 2005
    #12
  13. Frans Englich

    Jeff Shannon Guest

    Marc 'BlackJack' Rintsch wrote:

    > Frans Englich wrote:
    >
    >>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?

    >
    > 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__().


    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
     
    Jeff Shannon, Jan 18, 2005
    #13
  14. Frans Englich

    top Guest

    Jeff Shannon wrote:
    > class __Foo:
    > "I am a singleton!"
    > pass
    >
    > def Foo(foo_obj = __Foo()):
    > assert isinstance(foo_obj, __Foo
    > return foo_obj

    this is a bit simpler, I think, and takes advantage from Python's free
    name-rebinding.

    class Singleton(object):
    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
     
    top, Feb 12, 2005
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ralf W. Grosse-Kunstleve
    Replies:
    16
    Views:
    620
    Lonnie Princehouse
    Jul 11, 2005
  2. Ralf W. Grosse-Kunstleve
    Replies:
    18
    Views:
    621
    Bengt Richter
    Jul 11, 2005
  3. Ralf W. Grosse-Kunstleve
    Replies:
    2
    Views:
    428
    Dan Sommers
    Jul 12, 2005
  4. falcon
    Replies:
    0
    Views:
    402
    falcon
    Jul 31, 2005
  5. Bart Kastermans
    Replies:
    6
    Views:
    425
    Bart Kastermans
    Jul 13, 2008
Loading...

Share This Page