How does Mr. Martelli's Borg recipe work ?

M

Mars

I have looked long and hard at Mr. Martelli's Borg recipe:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531

It is a very useful substitute for a Singleton, but I can't figure out
how it works. _shared_state is never assigned any value, only
self.__dict__ is assigend self._shared_sate's value - or rather
(self.)_shared_state must be assigned a value at some point, otherwise
the code wouldn't work(right ?). I have gone through the code in the
debugger several(many) times, and inserted god knows how many print
statements, but without luck(or skill i guess).

Just so you don't waste your time, i do understand that _shared_state
is a class field(variable, or what not) and that its state is shared
by all instances.

Sorry if this question is banal, but I really need to understand this,
even if it means exposing my complete lack of understanding as far as
what is probaly basic Python knowledge.

Regards,

Martin
 
T

Terry Reedy

Mars said:
I have looked long and hard at Mr. Martelli's Borg recipe:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531

It is a very useful substitute for a Singleton, but I can't figure out
how it works. _shared_state is never assigned any value, only
self.__dict__ is assigend self._shared_sate's value - or rather
(self.)_shared_state must be assigned a value at some point, otherwise
the code wouldn't work(right ?).

Yes and no. The 'value' of a name is the object it is assigned to.
In the last line of the 4 line code and only body line of the __init__

class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state

the instance name '__dict__' is rebound to the object also called
__shared_state, so that the two names become aliases for the *same*
object (of type dict). The original instance dict gets unbound from
the name__dict__ and becomes eligible to be garbage collected. The
same is true for every Borg instance. Create 100 Borg instances and
there are 101 aliases for one and the same dict.

Now ,
instance.name = value
is (usually) executed behind the scence as
instance.__dict__['name'] = value
where __dict__ is the dict *currently* bound to instance attribute
__dict__. (One of the exceptions to this is _dict__ itself.) In the
Borg scheme, that name is no longer bound to the original dict but to
the shared dict. So nothing is (or at least need be) ever added to
that dict under its first name of __shared_state.

Terry J. Reedy
 
I

Ian Bicking

I have looked long and hard at Mr. Martelli's Borg recipe:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531

It is a very useful substitute for a Singleton, but I can't figure out
how it works.

While I don't want to discourage you from learning more about the
internals of Python objects (certainly a worthwhile goal), "singletons"
are usually thought about too hard by people new to Python. This is a
good way to make a singleton:

class _Something:
...

TheSomething = _Something()


Then just never refer to _Something again. Import TheSomething (calling
it whatever you want), and use it, not its class. It's a singleton
because there's only one of them. Simplicity!

Ian
 
R

Robin Becker

Ian Bicking said:
class _Something:
...

TheSomething = _Something()


Then just never refer to _Something again. Import TheSomething (calling
it whatever you want), and use it, not its class. It's a singleton
because there's only one of them. Simplicity!

Ian
.....can't one do
class TheSomething:
....

TheSomething = TheSomething()

then it's harder to get at the class which presumably is now only
available as TheSomething.__class__
 
E

Erik Max Francis

Robin said:
....can't one do
class TheSomething:
....

TheSomething = TheSomething()

then it's harder to get at the class which presumably is now only
available as TheSomething.__class__

Well, you can name the class Hugahglaugahglaugalgha, or delete the
original class name explicitly, or any such thing. The point is that in
Python, if you're accessing a name starting with underscores in
something you don't own, you probably shouldn't be. "We're all adults
here."
 
I

Ian Bicking

....can't one do
class TheSomething:
....

TheSomething = TheSomething()

then it's harder to get at the class which presumably is now only
available as TheSomething.__class__

I wouldn't generally name the class and the singleton the same, but just
because that is confusing -- if you look briefly at the code you'll
initially think that TheSomething is a class, when it is actually an
instance. Better would be:

class _Something:
....

TheSomething = _Something()
del _Something

Ian
 
S

Steven Taschuk

Quoth John Roth:
[...]
I can kind of understand the justification for the Borg pattern
in Python releases before 2.2, because there was no way of
creating a true singleton in those releases. However, in 2.2 and
later, it's really easy to create one using new style classes.
[...implementing singletons with __new__...]
That being the case, I'd like to see the Borg pattern go the way
of a fondly remembered hack that is no longer necessary.

Just out of curiosity: why do you prefer singletons to Borgs in
the first place?

(I don't see Borg as a hack to get the behaviour of a singleton; I
see it as a more direct way to solve the problem which singletons
are supposed to solve. Thus to me Borg is actually preferable, in
those exceedingly rare cases when that problem actually arises.)
 
B

Bengt Richter

Quoth John Roth:
[...]
I can kind of understand the justification for the Borg pattern
in Python releases before 2.2, because there was no way of
creating a true singleton in those releases. However, in 2.2 and
later, it's really easy to create one using new style classes.
[...implementing singletons with __new__...]
That being the case, I'd like to see the Borg pattern go the way
of a fondly remembered hack that is no longer necessary.

Just out of curiosity: why do you prefer singletons to Borgs in
the first place?

(I don't see Borg as a hack to get the behaviour of a singleton; I
see it as a more direct way to solve the problem which singletons
are supposed to solve. Thus to me Borg is actually preferable, in
those exceedingly rare cases when that problem actually arises.)
How about just

import zerolengthfile as borginstancename

and using it? E.g.,

[14:04] C:\pywk\clp>dir zer*, a.* b.*
<snips>
03-07-23 13:50 0 zero_len.py
03-07-23 14:01 28 a.py
03-07-23 14:02 28 b.py

[14:05] C:\pywk\clp>type a.py
import zero_len as aborg


[14:05] C:\pywk\clp>type b.py
import zero_len as bborg


[14:05] C:\pywk\clp>python
Python 2.2.2 (#37, Oct 14 2002, 17:02:34) [MSC 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import zero_len as inter
>>> import a
>>> import b
>>> dir() ['__builtins__', '__doc__', '__name__', 'a', 'b', 'inter']
>>> dir(a) ['__builtins__', '__doc__', '__file__', '__name__', 'aborg']
>>> dir(b) ['__builtins__', '__doc__', '__file__', '__name__', 'bborg']
>>> inter.x = 123
>>> a.aborg.x 123
>>> b.bborg.x 123
>>> b.bborg.y = 456
>>> a.aborg.y 456
>>> inter.y 456
>>>

Regards,
Bengt Richter
 
S

Steven Taschuk

Quoth Bengt Richter:
[borg vs singleton]
How about just

import zerolengthfile as borginstancename

and using it? E.g., [...]

That would be fine in many cases, I'm sure.

Modules don't do properties (or other descriptor magic), though.
 
B

Bob Gailer

I have at times had the need to initialize some things once at the class
level, and have resorted to techniques like:

class foo:
first = True
def __init__(self):
if foo.first:
foo.first = False
# initialization code

Then a "better idea" occurred:

class foo:
def __init__(self):
del foo.__init__
# initialization code

Bob Gailer
(e-mail address removed)
303 442 2625
 
B

Ben Finney

I have at times had the need to initialize some things once at the
class level

In Python 2.2 (earlier?) you can define any attribute at the class
level, and it will be shared by all instances:

class Foo( object ):
grumble = 0.1
flibble = {}
def __init__( self ):
''' Instance initialisation '''
return

This causes the attributes 'grumble', 'flibble', and '__init__()' to be
shared by all Foo instances; anything done within __init__() will affect
the individual instance only.
 
B

Bengt Richter

Quoth Bengt Richter:
[borg vs singleton]
How about just

import zerolengthfile as borginstancename

and using it? E.g., [...]

That would be fine in many cases, I'm sure.

Modules don't do properties (or other descriptor magic), though.
Not insurmountable ;-)

====< propmod.py >==========================================
class PropModNames(object):
def __get__(self, ob, cls=None):
if ob is None: return self
return 'Shared names of propmod: %r' % vars(ob).keys()
def __set__(self, ob, val): raise AttributeError, 'names property is read-only'
def __delete__(self, ob, val): raise AttributeError, 'names property is read-only'

class SetPropModProp(object):
def __init__(self): self.name2use = None
def __get__(self, ob, cls=None):
if ob is None: return self
if not self.name2use:
props = [k for k,v in vars(ob.__class__).items()
if not k.startswith('_') and (
hasattr(v, '__get__') or hasattr(v, '__set__'))]
props.sort()
return 'Properties of propmod: %r' % props
else:
return getattr(ob.__class__, self.name2use)
def __set__(self, ob, nameNprop):
if isinstance(nameNprop,str): self.name2use = nameNprop
elif len(nameNprop)==1: delattr(ob.__class__, nameNprop[0]) # (name,) means delete
else: name, prop = nameNprop; setattr(ob.__class__, name, prop)

class PropMod(object):
names = PropModNames()
properties = SetPropModProp() # expects propmod.setprop = name, property or name2use
def __init__(self): __import__('sys').modules['propmod'] = self
============================================================

===< apm.py >============
import propmod as zz
=========================

The first lines below binds the real propmod module temporarily.
The second line binds the magic propmod locally and makes it available
(by PropMod.__init__ side effect ) for import by anyone else as
an ordinary (looking) "import propmod." (Note what apm.py does).

I thought it cute to make a property that is a kind of gateway to
the class attribute space, so that one can use the .properties attribute
of the propmod module to list, store, retrieve, and delete properties -- as well
as arbitrary class variables...
>>> import propmod
>>> propmod = propmod.PropMod()
>>> propmod.properties "Properties of propmod: ['names', 'properties']"
>>> propmod.names 'Shared names of propmod: []'
>>> propmod.x = 123
>>> propmod.y = 456
>>> propmod.names "Shared names of propmod: ['y', 'x']"
>>> propmod.properties "Properties of propmod: ['names', 'properties']"
>>> propmod.properties = ('hello', property(lambda self:'Hello properties!'))
>>> propmod.properties "Properties of propmod: ['hello', 'names', 'properties']"
>>> propmod.hello 'Hello properties!'
>>> import apm
>>> apm.zz.properties "Properties of propmod: ['hello', 'names', 'properties']"
>>> apm.zz.hello 'Hello properties!'
>>> apm.zz.z = 'z via apm.zz.z'
>>> propmod.z 'z via apm.zz.z'
>>> apm.zz.names
"Shared names of propmod: ['y', 'x', 'z']"

Not to go on and on, but ...
>>> apm.zz.properties = ('hello',)
>>> propmod.properties "Properties of propmod: ['names', 'properties']"
>>> propmod.properties = 'names'
>>> propmod.properties
>>> propmod.properties = ''
>>> propmod.properties "Properties of propmod: ['names', 'properties']"
>>> propmod.properties = ('classvar', 'classvar value')
>>> apm.zz.properties "Properties of propmod: ['names', 'properties']"
>>> apm.zz.classvar 'classvar value'
>>> apm.zz.__class__
>>> apm.zz.__class__.__dict__.keys() ['__module__', 'names', '__dict__', 'classvar', '__weakref__', 'properties', '__init__', '__doc__']
>>> apm.zz.__class__.__dict__['classvar'] 'classvar value'
>>> apm.zz.classvar = 'obj attr'
>>> propmod.names "Shared names of propmod: ['y', 'x', 'z', 'classvar']"
>>> propmod.classvar 'obj attr'
>>> del propmod.classvar
>>> propmod.classvar
'classvar value'

Regards,
Bengt Richter
 
B

Bengt Richter

Quoth Bengt Richter:
[borg vs singleton]
How about just

import zerolengthfile as borginstancename

and using it? E.g., [...]

That would be fine in many cases, I'm sure.

Modules don't do properties (or other descriptor magic), though.
Not insurmountable ;-)

====< propmod.py >========================================== [...]
I thought it cute to make a property that is a kind of gateway to
the class attribute space, so that one can use the .properties attribute
of the propmod module to list, store, retrieve, and delete properties -- as well
as arbitrary class variables...

Of course,

propmod.__class__.xxx = yyy

works as well as

propmod.properties = 'xxx', yyy

so it's kind of a silly exercise, but it does demo properties for a sharable "module."

A much sparer approach:
>>> import sys
>>> sys.modules['simple'] = type('SimpleMod',(),{})()
>>> import simple
>>> simple.x = 123
>>> simple.__class__.hi = property(lambda self:'Hi ho')
>>> simple.x 123
>>> simple.hi 'Hi ho'
>>> file('impsimp.py','w').write('import simple as m\n')
>>> import impsimp
>>> impsimp.m.hi 'Hi ho'
>>> impsimp.m.x
123

Regards,
Bengt Richter
 
I

Ian Bicking

In Python 2.2 (earlier?) you can define any attribute at the class
level, and it will be shared by all instances:

This is true in all versions of Python. However, there are some
instances where you can't initialize the attributes at class creation
time, and you want to delay initializing those variables until some
later time. This usually is a problem of circular dependencies, or
where initialization somehow depends on the overall context of the
program (like configuration variables that may not be fully read by the
time the module is imported).

Ian
 

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,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top