使用元类和装饰器实现动态基类工厂

91 阅读3分钟

在Python中,可以使用类工厂来创建新的类。类工厂通常是一个函数或类方法,它接收一个或多个参数,并返回一个新的类。类工厂的一个典型应用是创建具有不同状态或行为的类的子类。

在下面的代码示例中,我们有一个名为EntityBase的基类,它为所有其他实体类提供了一组公共方法和属性。我们还定义了一个entity()函数,它可以用来创建一个新的实体类。entity()函数接收一个参数,该参数指定新实体类的名称。entity()函数返回一个新的类,该类继承自EntityBase类,并具有一个名为__entity__的特殊属性,该属性的值等于新实体类的名称。

class EntityBase (object) :
    __entity__ = None

    def __init__ (self) :
        pass

def entity (name) :
    class Entity (EntityBase) :
        __entity__ = name

        def __init__ (self) :
            pass

    return Entity

现在,我们可以使用entity()函数来创建新的实体类。例如,下面的代码创建一个名为Smth的新实体类:

class Smth (entity ("SMTH")) :
    def __init__ (self, a, b) :
        self.a = a
        self.b = b

Smth类继承自EntityBase类,并具有一个名为__entity__的特殊属性,该属性的值等于"SMTH"

2、解决方案

我们可以使用元类和装饰器来实现动态基类工厂。

元类方法

我们可以使用元类来跟踪已定义的类。当定义了一个具有该元类的类时,将调用Register.__init__。我们可以在元类中添加一个名为registry的字典,并将类名和对象添加到该字典中。这样,我们就可以在以后直接查找类。

registry = {} # dict of subclasses

def get_entity( name ):
    return registry[name]    

class Register(type):
    def __init__(cls, name, bases, dict):
        registry[name] = cls
        type.__init__(cls,name, bases, dict)

class EntityBase(object):
    __metaclass__ = Register

class OneThing(EntityBase):
    pass

class OtherThing(OneThing):
    pass

print registry # dict with Entitybase, OneThing, OtherThing
print get_entity("OtherThing") # <class '__main__.OtherThing'>

装饰器方法

我们也可以使用装饰器来实现动态基类工厂。装饰器是一个函数,它接收一个函数作为参数,并返回一个新的函数。新的函数具有与原始函数相同的功能,但可能会在原始函数执行之前或之后执行一些额外的代码。

def register(entity):
    def decorator(subclass):
        subclass.__entity__ = entity
        return subclass
    return decorator

@register('Smith')
class Smith(EntityBase):
     def __init__(self, a, b):
         self.a = a
         self.b = b

s = factory('Smith')(1, 2)

上面的代码中,register()装饰器接收一个参数entity,该参数指定新实体类的名称。register()装饰器返回一个名为decorator()的函数,该函数接收一个类作为参数,并返回一个新的类。新的类具有与原始类相同的功能,但具有一个名为__entity__的特殊属性,该属性的值等于新实体类的名称。

我们可以使用@register()装饰器来装饰实体类。例如,下面的代码使用@register()装饰器装饰了Smith类:

@register('Smith')
class Smith(EntityBase):
     def __init__(self, a, b):
         self.a = a
         self.b = b

Smith类具有一个名为__entity__的特殊属性,该属性的值等于"Smith"

现在,我们可以使用factory()函数来创建新的实体类。例如,下面的代码创建一个名为s的新实体类:

s = factory('Smith')(1, 2)

s类继承自EntityBase类,并具有一个名为__entity__的特殊属性,该属性的值等于"Smith"

元类和装饰器都可以用来实现动态基类工厂。元类方法更灵活,但装饰器方法更简单。