Use of metaclasses to hide access to class constructor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | """
USAGE:
class A:
__metaclass__ = Singleton
def __init__(self):
self.a=1
a=A()
b=A()
a is b #true
You don't have access to the constructor,
you only can call a factory that returns always the same instance.
"""
_global_dict = {}
def Singleton(name, bases, namespace):
class Result:pass
Result.__name__ = name
Result.__bases__ = bases
Result.__dict__ = namespace
_global_dict[Result] = Result()
return Factory(Result)
class Factory:
def __init__(self, key):
self._key = key
def __call__(self):
return _global_dict[self._key]
def test():
class A:
__metaclass__ = Singleton
def __init__(self):
self.a=1
a=A()
a1=A()
print "a is a1", a is a1
a.a=12
a2=A()
print "a.a == a2.a == 12", a.a == a2.a == 12
class B:
__metaclass__ = Singleton
b=B()
a=A()
print "a is b",a==b
|
Metaclasses are a powerfull tool to change behaviour to classes. To use metaclasses you must provide a callable object that accepts 3 arguments: name, a tuple of base classes and a namespace. class Dummy:pass def x(*args): print args return Dummy
class A(B): a=1 __metaclass__ = x
When the code is compiled x is called and prints: ('A',(B,),{'a':1}) if you do:
A
The big problem is that a class A can't be subclassed. If you want to subclass: class _A: def a(self):return "a" class A(_A): ___metaclass__=Singleton class _B(_A): def b(self):return "b" class B(_B): __metaclass__ = Singleton
This simplifies things and allows subclassing.
Even better: this is probably as simple as it gets...
...and it doesn't work. The previous examples give the correct test output, but don't function as actual classes. Doh!
I'll have a think about this and post back once I've tested it properly.
Sorry for the noise!
This one works! Silly really... in the last example, I was returning a class rather than an object from my factory.
Class variables and __metaclass__.
Here's a version that copes with your code. The problem is that the Singleton is trying to construct an instance of your class before the class has finished being fully defined. Hence, you don't get access to a bound variable for your class.
Here's a version that gets around that problem, although it adds a line or two more of code:
One small recommended change. Use 'super' to make your metaclass safe for multiple inheritance:
This is not inheritance safe. If you do
Michele
There is an even easier way too, which is also inheritance safe:
Use it like normal:
The use of self.__dict__ is very important to make this work correctly. It ensures we only create one instance per class definition and do not see the instance of a parent class by mistake.