前言在 Python 里面大家都比较熟悉了,通过 class 关键字创建一个类,这是通过硬编码来实现的。 那么如何动态创建一个类呢,如果给一批数据,让它动态生成一个类? 学习警告:不要轻易打开潘多拉的魔盒,潘多拉出于好奇打开一个魔盒, 释放出人世间的所有邪恶:贪婪、虚无、诽谤、嫉妒、痛苦等等,当她再盖上盒子时,只剩下希望在里面。 不要轻易去开启python的 黑魔法—元类(metaclass)学习,可能会有2个极端 类与实例先来理解一个非常简单的例子 class People: name = "zhangsan" age = 22
p = People() print(p.name) print(p.age)
上面代码 People 是一个类, p是 People 类的实例。 接着用 type 查看对象 print(type(p)) # <class '__main__.People'> print(type(People)) # <class 'type'>
p 是 People 类的实例 People 是一个类,它是 type 的实例,也就是说 People 类是 type 创建的一个实例。 type 就是一个元类(metaclass),简单的理解,元类就是创建类的类。 再举个简单例子 x = 123 print(type(x)) # <class 'int'> print(type(int)) # <class 'type'>
y = "abc" print(type(y)) # <class 'str'> print(type(str)) # <class 'type'>
学到这里也就理解了,python是面向对象的编程语言,python里面的str, int 等class 创建的类,都是type 类创建的,type 就是一个创建类的元类(metaclass)。 str, int 等class 创建的类都是 type 类的实例。 用一个图来表示对象(obj,或叫实例)、类(class)、元类(Metaclass)的关系。
(可以这样理解:张三是人类的一个实例,人类是上帝创造的,那么人类是上帝的一个实例, type 就是 python 里面的上帝) type 动态创建类type 创建类的部分源码 def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__ """ type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type # (copied from class doc) """ pass
基本语法如下: type(name of the class, tuple of the parent class (for inheritance, can be empty), dictionary containing attributes names and values)
传三个参数: 接着我们用 type 动态创建一个类 # 通过 type 创建一个猫类 Cat = type("Cat", (object, ), {"name": "hello kitty", "age": 2}) c = Cat() print(c.name) print(c.age) print(type(c)) # <class '__main__.Cat'> print(type(Cat)) # <class 'type'>
Cat 就是 type 创建的一个类,等价于自己写的class Cat , 它是type 类的实例 c 是 Cat 类的实例。 学到这,就是掌握了使用 type 动态创建类的入门学习了~ 自定义元类(metaclass)如果想把一个类设计成 MetaClass 元类,其必须符合以下条件: class DemoMetaClass(type): def __init__(cls, what, bases=None, dict=None): """ 初始化,四个参数 """ print("metaclass 创建类初始化。。。。") super().__init__(what, bases, dict)
def __new__(cls, name, bases, attrs): """ 创建类,四个参数 cls 代表动态修改的类 name 代表动态修改的类名 bases 代表被动态修改的类的所有父类 attr 代表被动态修改的类的所有属性、方法组成的字典 """ # 动态为该类添加一个name属性 attrs['name'] = "zhangsan" attrs['age'] = lambda x: 20 return super().__new__(cls, name, bases, attrs)
# 定义一个类,指定metaclass 元类创建,而不是由 type 创建 class NewDemo(object, metaclass=DemoMetaClass): pass
以上代码直接执行,会看到打印结果 metaclass 创建类初始化。。。。
上面代码中 DemoMetaClass 是一个类, 该类继承自 type 类,并且内部实现了 __new__() 方法,因此 DemoMetaClass 是一个元类。 NewDemo 类不再由默认的type 创建了,而是由自己写的 DemoMetaClass 来创建。 当使用class NewDemo 创建类时,DemoMetaClass 元类就会触发__init__ 初始化 和 __new__ 创建类。 同样的道理,当我们写一个类, 继承了NewDemo (它由DemoMetaClass 创建) class Hello(NewDemo): pass
它也会触发元类 __new__ 创建类 和 __init__ 初始化。 还可以由 type 动态创建类 World = type("World", (NewDemo, ), {}) w = World() print(w.name) # zhangsan print(w.age()) # 20
它也会触发元类 __new__ 创建类 和 __init__ 初始化 。 学到这,大家会发现目前自己创建的元类已经改变了python 创建类的默认操作,潘多拉的魔盒被你悄悄打卡了~~ 掌握 __init__ 和 __new__ 如果创建的class 类里面也有__init__ 和 __new__ , 看下执行过程是怎样的 class DemoMetaClass(type): def __init__(cls, what, bases=None, dict=None): """ 初始化,四个参数 """ print("metaclass 创建类初始化。。。。") super().__init__(what, bases, dict)
def __new__(cls, name, bases, attrs): """ 创建类,四个参数 cls 代表动态修改的类 name 代表动态修改的类名 bases 代表被动态修改的类的所有父类 attr 代表被动态修改的类的所有属性、方法组成的字典 """ # 动态为该类添加一个name属性 attrs['name'] = "zhangsan" attrs['age'] = lambda x: 20 print(f"metaclass __new__: {cls}, {name}, {bases}, {attrs}") return super().__new__(cls, name, bases, attrs)
# 定义一个类,指定metaclass 元类创建,而不是由 type 创建 class NewDemo(object, metaclass=DemoMetaClass): def __init__(self, x): self.x = x print(f"class __init__ : {self.x}")
def __new__(cls, *args, **kwargs): # 通过 new 来实例化 __init__.new 是用来创建实例的。 print(f"class __new__:{cls}, {args}, {kwargs}") return object.__new__(cls)
print("------------------------------------------") demo = NewDemo(x="yoyo")
运行结果 metaclass __new__: <class '__main__.DemoMetaClass'>, NewDemo, (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'NewDemo', '__init__': <function NewDemo.__init__ at 0x000002039EDB9310>, '__new__': <function NewDemo.__new__ at 0x000002039EDB93A0>, 'name': 'zhangsan', 'age': <function DemoMetaClass.__new__.<locals>.<lambda> at 0x000002039EDB9430>} metaclass 创建类初始化。。。。 --------------------------- class __new__:<class '__main__.NewDemo'>, (), {'x': 'yoyo'} class __init__ : yoyo
元类(metaclass) 实例—-yaml.YAMLObjectyaml.YAMLObject 类用到了metaclass, 相关源码如下 class YAMLObjectMetaclass(type): """ The metaclass for YAMLObject. """ def __init__(cls, name, bases, kwds): super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds) if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None: if isinstance(cls.yaml_loader, list): for loader in cls.yaml_loader: loader.add_constructor(cls.yaml_tag, cls.from_yaml) else: cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
cls.yaml_dumper.add_representer(cls, cls.to_yaml)
class YAMLObject(metaclass=YAMLObjectMetaclass): """ An object that can dump itself to a YAML stream and load itself from a YAML stream. """
__slots__ = () # no direct instantiation, so allow immutable subclasses
yaml_loader = [Loader, FullLoader, UnsafeLoader] yaml_dumper = Dumper
yaml_tag = None yaml_flow_style = None
@classmethod def from_yaml(cls, loader, node): """ Convert a representation node to a Python object. """ return loader.construct_yaml_object(node, cls)
@classmethod def to_yaml(cls, dumper, data): """ Convert a Python object to a representation node. """ return dumper.represent_yaml_object(cls.yaml_tag, data, cls, flow_style=cls.yaml_flow_style)
pydantic 也用到了metaclasspydantic 的基本使用 from datetime import datetime from typing import List, Optional from pydantic import BaseModel
class User(BaseModel): id: int name = 'yo yo' birth: Optional[datetime] = None friends: List[int] = []
external_data = { 'id': '123', 'birth': '2019-06-01 12:22', 'friends': [1, 2, '3'], } user = User(**external_data) print(user.dict()) # dict() 函数将对象转化成字典
BaseModel 类,就是通过自定义的metaclass创建,相关源码 class BaseModel(Representation, metaclass=ModelMetaclass): if TYPE_CHECKING: # populated by the metaclass, defined here to help IDEs only __fields__: Dict[str, ModelField] = {} __validators__: Dict[str, AnyCallable] = {} __pre_root_validators__: List[AnyCallable] __post_root_validators__: List[Tuple[bool, AnyCallable]] __config__: Type[BaseConfig] = BaseConfig __root__: Any = None __json_encoder__: Callable[[Any], Any] = lambda x: x __schema_cache__: 'DictAny' = {} __custom_root_type__: bool = False __signature__: 'Signature' __private_attributes__: Dict[str, Any] __class_vars__: SetStr __fields_set__: SetStr = set()
Config = BaseConfig __slots__ = ('__dict__', '__fields_set__') __doc__ = '' # Null out the Representation docstring
def __init__(__pydantic_self__, **data: Any) -> None: """ Create a new model by parsing and validating input data from keyword arguments.
Raises ValidationError if the input data cannot be parsed to form a valid model. """ # Uses something other than `self` the first arg to allow "self" as a settable attribute values, fields_set, validation_error = validate_model(__pydantic_self__.__class__, data) if validation_error: raise validation_error try: object_setattr(__pydantic_self__, '__dict__', values) except TypeError as e: raise TypeError( 'Model values must be a dict; you may not have returned a dictionary from a root validator' ) from e object_setattr(__pydantic_self__, '__fields_set__', fields_set) __pydantic_self__._init_private_attributes()
ModelMetaclass 元类相关源码 class ModelMetaclass(ABCMeta): @no_type_check # noqa C901 def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901 fields: Dict[str, ModelField] = {} config = BaseConfig validators: 'ValidatorListDict' = {}
class ABCMeta(type): """Metaclass for defining Abstract Base Classes (ABCs).
Use this metaclass to create an ABC. An ABC can be subclassed directly, and then acts as a mix-in class. You can also register unrelated concrete classes (even built-in classes) and unrelated ABCs as 'virtual subclasses' -- these and their descendants will be considered subclasses of the registering ABC by the built-in issubclass() function, but the registering ABC won't show up in their MRO (Method Resolution Order) nor will method implementations defined by the registering ABC be callable (not even via super()). """ def __new__(mcls, name, bases, namespace, **kwargs): cls = super().__new__(mcls, name, bases, namespace, **kwargs) _abc_init(cls) return cls
def register(cls, subclass): """Register a virtual subclass of an ABC.
Returns the subclass, to allow usage as a class decorator. """ return _abc_register(cls, subclass)
元类用来创建 API 是非常好的方法,使用元类的编写对于 python 开发者来说很复杂, 但对于使用者可以非常简洁的调用 API。 学到这大概明白了,元类是给真正的python 开发者使用的(并不是会写个print 就是python开发者,这里对开发者的定义是能开发框架的开发者),而不是给 python 使用者用(python 使用者是会调用第三方库的人员,无框架开发能力。)
|