PHP前端开发

详细讲解Python中的元类及其用法

百变鹏仔 3小时前 #Python
文章标签 详细

1、用元类验证子类

每当我们定义新类的时候,元类就会运行雅正代码,以确保这个新类符合规定的规范。
python系统把子类的class语句处理完毕,就会调用元类的 __new__ 方法。元类可以通过 __new__ 方法,获取子类、孙子类的名称,父亲及属性。
这样使得我们不需要将验证代码放在本类 __init__ 方法中,等到构建对象再验证。

下例中,定义一个边数小于3的子类,class语句一结束,元类的验证代码就会拒绝这个class。

class ValidatePolygon(type):    def __new__(meta, name, bases, class_dict):        # Don't 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):    sides = None  # Specified by subclasses    @classmethod    def interior_angles(cls):        return (cls.sides - 2) * 180class Triangle(Polygon):    sides = 3print(Triangle.interior_angles())

2、用元类注册子类

每次从基类中继承子类时,基类的元类都可以自动运行注册代码。
这在需要反向查找 ‘reverse lookup’ 时很有用,使得在简单标识符和对应的类之间,建立映射关系。
依然利用的是class语句执行完,自动调用元类的 __new__ 方法。

import json registry = {}def register_class(target_class):    registry[target_class.__name__] = target_classdef deserialize(data):    params = json.loads(data)    name = params['class']    target_class = registry[name]    return target_class(*params['args'])class Meta(type):    def __new__(meta, name, bases, class_dict):        cls = type.__new__(meta, name, bases, class_dict)        register_class(cls)        return clsclass Serializable(object):    def __init__(self, *args):        self.args = args    def serialize(self):        return json.dumps({            'class': self.__class__.__name__,            'args': self.args,        })    def __repr__(self):        return '%s(%s)' % (            self.__class__.__name__,            ', '.join(str(x) for x in self.args))class RegisteredSerializable(Serializable, metaclass=Meta):    passclass Vector3D(RegisteredSerializable):    def __init__(self, x, y, z):        super().__init__(x, y, z)        self.x, self.y, self.z = x, y, zv3 = Vector3D(10, -7, 3)print('Before:    ', v3)data = v3.serialize()print('Serialized:', data)print('After:     ', deserialize(data))print(registry)

3、用元类注解类的属性

使用元类像是在 class 语句上放置了挂钩,class语句处理完毕,挂钩就会立刻触发。
下列中借助元类设置了 Filed.name 和 Filed.name。

class Field(object):    def __init__(self):        # These will be assigned by the metaclass.        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 clsclass DatabaseRow(object, metaclass=Meta):    passclass BetterCustomer(DatabaseRow):    first_name = Field()    last_name = Field()    prefix = Field()    suffix = Field()foo = BetterCustomer()print('Before:', repr(foo.first_name), foo.__dict__)foo.first_name = 'Euler'print('After: ', repr(foo.first_name), foo.__dict__)

元类总结就到这里,自己也没有完全理解清楚。
希望对此有深刻理解的pythoner留言。

立即学习“Python免费学习笔记(深入)”;

代码来自: