Assigning to self

F

Frans Englich

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
 
P

Peter Otten

Frans said:
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.:
.... 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
 
R

Reinhold Birkenfeld

Frans said:
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
 
J

John Roth

Frans Englich said:
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
 
F

Frans Englich

Frans said:
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.:
... 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
 
F

Frans Englich

Frans said:
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
 
F

Frans Englich

"Frans Englich" <[email protected]> wrote in message

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
 
P

Peter Otten

Frans said:
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
 
P

Peter Otten

Frans said:
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
 
F

Frans Englich

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
 
J

John Roth

Frans Englich said:
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
 
M

Marc 'BlackJack' Rintsch

Frans Englich said:
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
 
J

Jeff Shannon

Marc said:
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
 
T

top

Jeff said:
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,
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top