1 minute read

Effective Python Item 33: Validate Subclasses with Metaclasses

主要是用来检查 static field,因为 static field 是在 class top-level 定义的,而 metaclass 的 __new__ 是在 class top-level 跑完之后才运行的。例子:

class ValidatePolygon(type):
    def __new__(meta, name, bases, class_dict):
        # Do not validate the abstract Polygon class
        if bases != (object,):
            if class_dict['sides'] < 3:
                raise ValueError('Polygons need 3+ sides')

        return type.__new__(meta, name, bases, class_dict)

class Polygon(object, metaclass=ValidatePolygon):
    """
    The abstract class
    """

    sides = None

    @classmethod
    def interior_angles(cls):
        return (cls.sides - 2) * 180

class Triangle(Polygon):
    sides = 3

Effective Python Item 34: Register Class Existence with Metaclasses

用来 register 所有使用了该 metaclass 的 class。至于为什么要 register,书上的场景是:有一组 top-level 的 serialize / deserialize 的 function,满足条件 A 的 class 使用格式甲,满足条件 B 的 class 使用格式乙;而 serialize / deserialize 本身也要用到类名。这时候 register 就很有必要了。

例子:

registry = {}

def register_class(target_class):
    registry[target_class.__name__] = target_class

def deserialize(data):
    params = json.loads(data)
    name = params['class']
    target_class = registry[name]
    return target_class(*params['args'])

class BetterSerializable(object):
    def __init__(self, *args):
        self.args = args
   
    def serialize(self):
        return json.dumps({
            'class': self.__class__.__name__,
            'args': self.args,
        })

class Meta(type):
    def __new__(meta, name, bases, class_dict):
        cls = type.__new__(meta, name, bases, class_dict)
        register_class(cls)
        return cls

class RegisteredSerializable(BetterSerializable, metaclass=Meta):
    pass

class Vector3D(RegisteredSerializable):
    ...

Effective Python Item 35: Annotate Class Attributes with Metaclasses

与 descriptor 组合使用。descriptor 形式上也是 static field(当然用起来还是 member field),在 class top-level 定义。我们可以使用 metaclass 去修改 descriptor 的一些属性,最常见的用法就是省略掉 descriptor 的 name。

需要显式写 descriptor name 的例子:

class Field(object):
    """
    The descriptor 
    """
    def __init__(self, name):
        self.name = name
        self.internal_name = '_' + self.name
    
    def __get__(self, instance, instance_type):
        if instance is None: 
            return self
        return getattr(instance, self.internal_name, ”)
    
    def __set__(self, instance, value):
        setattr(instance, self.internal_name, value)

class Customer(object):
    first_name = Field('first_name')
    last_name = Field('last_name')
    prefix = Field('prefix')
    suffix = Field('suffix')

省略掉 descriptor name 的写法:

class Field(object):
    """
    The descriptor 
    """
    def __init__(self):
        self.name = None
        self.internal_name = None
    
    def __get__(self, instance, instance_type):
        if instance is None: 
            return self
        return getattr(instance, self.internal_name, ”)
    
    def __set__(self, instance, value):
        setattr(instance, self.internal_name, value)

class Meta(type):
    def __new__(meta, name, bases, class_dict):
        for key, value in class_dict.items():
            if isinstance(value, Field):
                value.name = key
                value.internal_name = '_' + key
        cls = type.__new__(meta, name, bases, class_dict)
        return cls

class HasField(object, metaclass=Meta):
    pass

class Customer(HasField):
    first_name = Field()
    last_name = Field()
    prefix = Field()
    suffix = Field()

Categories:

Updated:

Comments