开发者

python: defining registry in base class

开发者 https://www.devze.com 2023-01-14 22:14 出处:网络
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 populate

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消