python: defining registry in base class
I'm implementing enumeration using a base class that defines a variety of methods. The actual enumerations are subclasses of that, with no additional methods or attributes. (Each subclass is populated with its own values using the constructor defined in the base class).
I use a registry (a class attribute that stores all the instances of that class). Ideally, I'd like to avoid defining it in each subclass. Unfortunately, if I define it in the base class, all the subclasses will end up sharing the same registry.
What's a good approach here?
Below is the implementation in case it helps (it's based on @jchl comment in python enumeration class for ORM purposes).
class IterRegistry(type):
def __iter__(cls):
return开发者_如何学Go iter(cls._registry.values())
class EnumType(metaclass = IterRegistry):
_registry = {}
_frozen = False
def __init__(self, token):
if hasattr(self, 'token'):
return
self.token = token
self.id = len(type(self)._registry)
type(self)._registry[token] = self
def __new__(cls, token):
if token in cls._registry:
return cls._registry[token]
else:
if cls._frozen:
raise TypeError('No more instances allowed')
else:
return object.__new__(cls)
@classmethod
def freeze(cls):
cls._frozen = True
def __repr__(self):
return self.token
@classmethod
def instance(cls, token):
return cls._registry[token]
class Enum1(EnumType): pass
Enum1('a')
Enum1('b')
for i in Enum1:
print(i)
# not going to work properly because _registry is shared
class Enum2(EnumType): pass
As you already have a metaclass you might as well use it to put a add a separate _registry
attribute to each subclass automatically.
class IterRegistry(type):
def __new__(cls, name, bases, attr):
attr['_registry'] = {} # now every class has it's own _registry
return type.__new__(cls, name, bases, attr)
Marty Alchin has a very nice pattern for this: see his blog entry.
What if you share the same registry, but with sub-registries per class, i.e.
if cls.__name__ not in self._registry:
self._registry[cls.__name__] = {}
self._registry[cls.__name__][token] = cls
You actually don't even need cls.__name__
, you should be able to use cls
itself as key.
精彩评论