467,886 Members | 1,776 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 467,886 developers. It's quick & easy.

Is a Borg rebellion possible? (a metaclass question)

In my application, I make use of the Borg idiom, invented by Alex
Martelli.

class Borg(object):
'''Borg Idiom, from the Python Cookbook, 2nd Edition, p:273

Derive a class form this; all instances of that class will share
the
same state, provided that they don't override __new__; otherwise,
remember to use Borg.__new__ within the overriden class.
'''
_shared_state = {}
def __new__(cls, *a, **k):
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_state
return obj

----
This has worked very well so far, but is starting to impose some
unwanted constraints on my program design.

What I would like to do is, to put it figuratively, create a Borg
rebellion with various splinter groups. In concrete Python terms, I
would like to have

class MyClass(Borg, ...):
...

seven_of_nine = MyClass(...) # part of group "BORG"
two_of_nine = MyClass(...)

splinter1 = MyClass(..., group='splinter')
splinter2 = MyClass(..., group='splinter')

and have splinter 1 and splinter2 share the same state, but a
different state than the one shared by members of the BORG collective.

Any suggestions from the metaclass experts?

André

Sep 7 '07 #1
  • viewed: 1273
Share:
10 Replies
On Fri, 2007-09-07 at 12:31 +0000, André wrote:
In my application, I make use of the Borg idiom, invented by Alex
Martelli.

class Borg(object):
'''Borg Idiom, from the Python Cookbook, 2nd Edition, p:273

Derive a class form this; all instances of that class will share
the
same state, provided that they don't override __new__; otherwise,
remember to use Borg.__new__ within the overriden class.
'''
_shared_state = {}
def __new__(cls, *a, **k):
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_state
return obj

----
This has worked very well so far, but is starting to impose some
unwanted constraints on my program design.

What I would like to do is, to put it figuratively, create a Borg
rebellion with various splinter groups. In concrete Python terms, I
would like to have

class MyClass(Borg, ...):
...

seven_of_nine = MyClass(...) # part of group "BORG"
two_of_nine = MyClass(...)

splinter1 = MyClass(..., group='splinter')
splinter2 = MyClass(..., group='splinter')

and have splinter 1 and splinter2 share the same state, but a
different state than the one shared by members of the BORG collective.

Any suggestions from the metaclass experts?
You don't need a metaclass. Just turn _shared_state into a dictionary of
shared states, keyed by the group name:

class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

HTH,

--
Carsten Haese
http://informixdb.sourceforge.net
Sep 7 '07 #2
On Sep 7, 10:27 am, Carsten Haese <cars...@uniqsys.comwrote:
On Fri, 2007-09-07 at 12:31 +0000, André wrote:
In my application, I make use of the Borg idiom, invented by Alex
Martelli.
class Borg(object):
'''Borg Idiom, from the Python Cookbook, 2nd Edition, p:273
Derive a class form this; all instances of that class will share
the
same state, provided that they don't override __new__; otherwise,
remember to use Borg.__new__ within the overriden class.
'''
_shared_state = {}
def __new__(cls, *a, **k):
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_state
return obj
----
This has worked very well so far, but is starting to impose some
unwanted constraints on my program design.
What I would like to do is, to put it figuratively, create a Borg
rebellion with various splinter groups. In concrete Python terms, I
would like to have
class MyClass(Borg, ...):
...
seven_of_nine = MyClass(...) # part of group "BORG"
two_of_nine = MyClass(...)
splinter1 = MyClass(..., group='splinter')
splinter2 = MyClass(..., group='splinter')
and have splinter 1 and splinter2 share the same state, but a
different state than the one shared by members of the BORG collective.
Any suggestions from the metaclass experts?

You don't need a metaclass. Just turn _shared_state into a dictionary of
shared states, keyed by the group name:

class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

HTH,

--
Carsten Haesehttp://informixdb.sourceforge.net
Unfortunately, it fails. Here's what I tried, followed by the
traceback
class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

class MyClass(SplinterBorg):
def __init__(self, name):
self.name = name

a1 = MyClass('a')
a2 = MyClass('aa')
b1 = MyClass('b', group="B")
Traceback (most recent call last):
File "test.py", line 15, in <module>
b1 = MyClass('b', group="B")
TypeError: __init__() got an unexpected keyword argument 'group'
Sep 7 '07 #3
André wrote:
On Sep 7, 10:27 am, Carsten Haese <cars...@uniqsys.comwrote:
>On Fri, 2007-09-07 at 12:31 +0000, André wrote:
>>In my application, I make use of the Borg idiom, invented by Alex
Martelli.
class Borg(object):
'''Borg Idiom, from the Python Cookbook, 2nd Edition, p:273
Derive a class form this; all instances of that class will share
the
same state, provided that they don't override __new__; otherwise,
remember to use Borg.__new__ within the overriden class.
'''
_shared_state = {}
def __new__(cls, *a, **k):
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_state
return obj
----
This has worked very well so far, but is starting to impose some
unwanted constraints on my program design.
What I would like to do is, to put it figuratively, create a Borg
rebellion with various splinter groups. In concrete Python terms, I
would like to have
class MyClass(Borg, ...):
...
seven_of_nine = MyClass(...) # part of group "BORG"
two_of_nine = MyClass(...)
splinter1 = MyClass(..., group='splinter')
splinter2 = MyClass(..., group='splinter')
and have splinter 1 and splinter2 share the same state, but a
different state than the one shared by members of the BORG collective.
Any suggestions from the metaclass experts?
You don't need a metaclass. Just turn _shared_state into a dictionary of
shared states, keyed by the group name:

class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

HTH,

--
Carsten Haesehttp://informixdb.sourceforge.net

Unfortunately, it fails. Here's what I tried, followed by the
traceback
class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

class MyClass(SplinterBorg):
def __init__(self, name):
self.name = name

a1 = MyClass('a')
a2 = MyClass('aa')
b1 = MyClass('b', group="B")
Traceback (most recent call last):
File "test.py", line 15, in <module>
b1 = MyClass('b', group="B")
TypeError: __init__() got an unexpected keyword argument 'group'

Because your subclass signature is completely wrong. Why did you feel
you had to add an __init__() method to your subclass? This has two bvad
effects:

1. It stops the super-class's __init__() method from being called, and

2. It breaks the calling syntax specified in the superclass.

All you really need is to create your SplinterBorgs with appropriate
group names, you don't neef subclasses at all:

a1 = SplinterBorg(group="one")
a2 = SplinterBorg(group="two")

and so on.

If you want to create subclasses then they should work like this:

class MyBorg1(SplinterBorg):
def __init__(self):
SplinterBorg.__init__(self, group='borg1')

and so on, with a different group for each. But this seems over complicated.

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
Holden Web LLC/Ltd http://www.holdenweb.com
Skype: holdenweb http://del.icio.us/steve.holden
--------------- Asciimercial ------------------
Get on the web: Blog, lens and tag the Internet
Many services currently offer free registration
----------- Thank You for Reading -------------

Sep 7 '07 #4
On Fri, 2007-09-07 at 15:54 +0000, André wrote:
Unfortunately, it fails. Here's what I tried, followed by the
traceback
class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

class MyClass(SplinterBorg):
def __init__(self, name):
self.name = name
[...]
Traceback (most recent call last):
File "test.py", line 15, in <module>
b1 = MyClass('b', group="B")
TypeError: __init__() got an unexpected keyword argument 'group'
Indeed, if you have an __init__ method that shouldn't see the "group"
argument, you need a metaclass after all so you can yank the "group"
argument between __new__ and __init__. The following code seems to work,
but it's making my brain hurt:

class SplinterBorgMeta(type):
def __call__(cls, *args, **kwargs):
inst = cls.__new__(cls, *args, **kwargs)
kwargs.pop("group",None)
inst.__init__(*args,**kwargs)
return inst

class SplinterBorg(object):
__metaclass__ = SplinterBorgMeta
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

class MyClass(SplinterBorg):
def __init__(self, name):
self.name = name

The alternatives, as Steve just pointed out, would be not to subclass
SplinterBorg or to provide an __init__ method that expects a "group"
argument.

HTH,

--
Carsten Haese
http://informixdb.sourceforge.net
Sep 7 '07 #5
On Sep 7, 3:53 pm, Steve Holden <st...@holdenweb.comwrote:
André wrote:
On Sep 7, 10:27 am, Carsten Haese <cars...@uniqsys.comwrote:
On Fri, 2007-09-07 at 12:31 +0000, André wrote:
In my application, I make use of the Borg idiom, invented by Alex
Martelli.
class Borg(object):
'''Borg Idiom, from the Python Cookbook, 2nd Edition, p:273
Derive a class form this; all instances of that class will share
the
same state, provided that they don't override __new__; otherwise,
remember to use Borg.__new__ within the overriden class.
'''
_shared_state = {}
def __new__(cls, *a, **k):
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_state
return obj
----
This has worked very well so far, but is starting to impose some
unwanted constraints on my program design.
What I would like to do is, to put it figuratively, create a Borg
rebellion with various splinter groups. In concrete Python terms, I
would like to have
class MyClass(Borg, ...):
...
seven_of_nine = MyClass(...) # part of group "BORG"
two_of_nine = MyClass(...)
splinter1 = MyClass(..., group='splinter')
splinter2 = MyClass(..., group='splinter')
and have splinter 1 and splinter2 share the same state, but a
different state than the one shared by members of the BORG collective.
Any suggestions from the metaclass experts?
You don't need a metaclass. Just turn _shared_state into a dictionary of
shared states, keyed by the group name:
class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj
HTH,
--
Carsten Haesehttp://informixdb.sourceforge.net
Unfortunately, it fails. Here's what I tried, followed by the
traceback
class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj
class MyClass(SplinterBorg):
def __init__(self, name):
self.name = name
a1 = MyClass('a')
a2 = MyClass('aa')
b1 = MyClass('b', group="B")
Traceback (most recent call last):
File "test.py", line 15, in <module>
b1 = MyClass('b', group="B")
TypeError: __init__() got an unexpected keyword argument 'group'

Because your subclass signature is completely wrong. Why did you feel
you had to add an __init__() method to your subclass? This has two bvad
effects:

Because this is what I need to do currently with using the standard
Borg class, and is something I would like to be able to do. Here's a
simplified version of the current usage I have
=====
class Borg(object):
_shared_state = {}
def __new__(cls, *a, **k):
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_state
return obj

class BorgConsole(Borg, SingleConsole):
'''Every BorgConsole share a common state'''
def __init__(self, locals={}, filename="Crunchy console"):
SingleConsole.__init__(self, locals, filename=filename)

=====
and it is called via something like
a_console = BorgConsole(locals)

where locals is a previously defined dict. Note that a number of
such instances are created dynamically as the program is used.

What I would like to do is to be able to add a "page_id" (the Borg
group referred to my previous message), so that I could call

a_console = BorgConsole(locals, page_id)
where page_id would be an optional argument, defaulting to the BORG
group, as mentioned in my original post.

I must admit I did not try with keywords arguments ... and will not be
for a couple of hours while I am at work.

>
1. It stops the super-class's __init__() method from being called, and
I don't understand why this is so here, and not with the usual Borg
case as I used it...
2. It breaks the calling syntax specified in the superclass.

All you really need is to create your SplinterBorgs with appropriate
group names, you don't neef subclasses at all:

a1 = SplinterBorg(group="one")
a2 = SplinterBorg(group="two")

and so on.

If you want to create subclasses then they should work like this:

class MyBorg1(SplinterBorg):
def __init__(self):
SplinterBorg.__init__(self, group='borg1')

and so on, with a different group for each. But this seems over complicated.
I do need subclasses (I think) as my classes inherit from more than
the SplinterBorg class... I'll see what I can do following some of
your suggestions.

Thanks for the reply,

André
regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
Holden Web LLC/Ltd http://www.holdenweb.com
Skype: holdenweb http://del.icio.us/steve.holden
--------------- Asciimercial ------------------
Get on the web: Blog, lens and tag the Internet
Many services currently offer free registration
----------- Thank You for Reading -------------

Sep 7 '07 #6
On Sep 7, 4:00 pm, Carsten Haese <cars...@uniqsys.comwrote:
On Fri, 2007-09-07 at 15:54 +0000, André wrote:
Unfortunately, it fails. Here's what I tried, followed by the
traceback
class SplinterBorg(object):
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj
class MyClass(SplinterBorg):
def __init__(self, name):
self.name = name
[...]
Traceback (most recent call last):
File "test.py", line 15, in <module>
b1 = MyClass('b', group="B")
TypeError: __init__() got an unexpected keyword argument 'group'

Indeed, if you have an __init__ method that shouldn't see the "group"
argument, you need a metaclass after all so you can yank the "group"
argument between __new__ and __init__. The following code seems to work,
but it's making my brain hurt:

class SplinterBorgMeta(type):
def __call__(cls, *args, **kwargs):
inst = cls.__new__(cls, *args, **kwargs)
kwargs.pop("group",None)
inst.__init__(*args,**kwargs)
return inst

class SplinterBorg(object):
__metaclass__ = SplinterBorgMeta
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

class MyClass(SplinterBorg):
def __init__(self, name):
self.name = name

The alternatives, as Steve just pointed out, would be not to subclass
SplinterBorg or to provide an __init__ method that expects a "group"
argument.

HTH,
Thanks for your reply. I will try to adapt it and see if it works in
my case.

André
--
Carsten Haesehttp://informixdb.sourceforge.net

Sep 7 '07 #7
Carsten Haese wrote:
Indeed, if you have an __init__ method that shouldn't see the "group"
argument, you need a metaclass after all so you can yank the "group"
argument between __new__ and __init__. The following code seems to work,
but it's making my brain hurt:

class SplinterBorgMeta(type):
def __call__(cls, *args, **kwargs):
inst = cls.__new__(cls, *args, **kwargs)
kwargs.pop("group",None)
inst.__init__(*args,**kwargs)
return inst

class SplinterBorg(object):
__metaclass__ = SplinterBorgMeta
_shared_states = {}
def __new__(cls, *a, **k):
group = k.pop("group","BORG")
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_states.setdefault(group,{})
return obj

class MyClass(SplinterBorg):
def __init__(self, name):
self.name = name
I think I would probably write that as::
>>class SplinterBorgMeta(type):
... def __init__(cls, name, bases, bodydict):
... cls._shared_states = {}
... def __call__(cls, *args, **kwargs):
... group = kwargs.pop('group', None)
... inst = cls.__new__(cls, *args, **kwargs)
... inst.__dict__ = cls._shared_states.setdefault(group, {})
... inst.__init__(*args, **kwargs)
... return inst
...
>>class MyClass(object):
... __metaclass__ = SplinterBorgMeta
... def __init__(self, name):
... self.name = name
...
>>a = MyClass('a')
aa = MyClass('aa')
b = MyClass('b', group='b')
bb = MyClass('bb', group='b')
a.name, aa.name, b.name, bb.name
('aa', 'aa', 'bb', 'bb')

That is, I don't think there's really a need for __new__ if you're using
a metaclass. Just set the instance's __dict__ in the __call__ method of
the metaclass.

STeVe
Sep 7 '07 #8
On Fri, 2007-09-07 at 14:54 -0600, Steven Bethard wrote:
Carsten Haese wrote:
[slightly convoluted example...]

I think I would probably write that as::
[concise example...]

That is, I don't think there's really a need for __new__ if you're using
a metaclass. Just set the instance's __dict__ in the __call__ method of
the metaclass.
Good point. I suppose it shows that this is the first metaclass I've
ever written, but it works, and it didn't make my brain explode ;)

--
Carsten Haese
http://informixdb.sourceforge.net
Sep 8 '07 #9
On Sep 7, 1:53 pm, Steve Holden <st...@holdenweb.comwrote:
All you really need is to create your SplinterBorgs with appropriate
group names, you don't neef subclasses at all:
Dang. With that subject heading I thought this was about some post-
Singularity, Python-programmed cyborgs rising up to take over the
universe.

See, e.g.
<a href="http://www.amazon.com/How-Survive-Robot-Uprising-Defending/dp/
1582345929/inscape-20">How To Survive A Robot Uprising</a>

I am officially misled!

rd

Sep 8 '07 #10
On Sep 7, 1:53 pm, Steve Holden <st...@holdenweb.comwrote:
All you really need is to create your SplinterBorgs with appropriate
group names, you don't neef subclasses at all:
oops, I tried this once and the link broke. I'll try tinyurl.

Dang. With that subject heading I thought this was about some post-
Singularity, Python-programmed cyborgs rising up to take over the
universe.

See, e.g.
How To Survive A Robot Uprising
http://tinyurl.com/yrk5pw

I am officially misled!

rd
Sep 8 '07 #11

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

5 posts views Thread by Irmen de Jong | last post: by
33 posts views Thread by Jacek Generowicz | last post: by
6 posts views Thread by Steven D'Aprano | last post: by
14 posts views Thread by Pedro Werneck | last post: by
2 posts views Thread by Chuck B | last post: by
5 posts views Thread by Lie | last post: by
reply views Thread by MrMoon | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.