像
dataclasses
和 pydantic
这样的工具如何为它们创建的类创建 __init__()
函数?
我知道我可以使用这些工具,但我想学习如何使用 python 元编程来解析我的类并根据属性及其类型创建动态初始化函数。
举个例子:
class MyClass:
x: int
y: str | None
z: float = 0.1
# I don't want to manually make this
def __init__(self, x: int, y: str | None, z: float = 0.1):
self.x = x
self.y = y
self.z = z
是否可以使用元类和类型注释做类似的事情?
主要有两种方法:
dataclass
的想法是,您需要以某种方式获取所有注释并从字符串手动build该方法(我将向您展示)。在这种情况下,您已经创建了类对象。
可用的工具有:
typing
模块具有get_type_hints
功能。__dict__
访问类的属性。exec()
动态创建对象。 (数据类确实是这样做的)from typing import get_type_hints
def my_dataclass(cls):
fn_definition = "def __init__(self, "
# building the first line of __init__
for annotation, type_ in get_type_hints(cls).items():
type_name = type_.__name__ if hasattr(type_, "__name__") else str(type_)
fn_definition += f"{annotation}: {type_name}"
if annotation in cls.__dict__:
fn_definition += f" = {cls.__dict__[annotation]}"
else:
fn_definition += ", "
fn_definition += "):\n"
# building the body of __init__
for annotation in get_type_hints(cls):
fn_definition += f"\tself.{annotation} = {annotation}\n"
print(fn_definition)
# creating the function object:
namespace = {}
exec(fn_definition, {}, namespace)
__init__ = namespace["__init__"]
cls.__init__ = __init__
return cls
@my_dataclass
class MyClass:
x: int
y: str | None
z: float = 0.1
obj = MyClass(10, 20)
print(obj.x)
print(obj.y)
print(obj.z)
输出:
def __init__(self, x: int, y: str | None, z: float = 0.1):
self.x = x
self.y = y
self.z = z
10
20
0.1
当然,这是一种幼稚的方法,并且存在一些问题,但它为您提供了该想法的概述。
dataclass
和 pydantic
(我将在后面展示)还做了很多其他事情。
Pydantic 使用的另一种方法是使用元类。不同之处在于,使用装饰器时,首先创建类,然后提取所需的信息,但在元类中,该信息可在
namespace
的 __new__
参数中获得,“在创建该类之前”:(我赢了不去实现了,因为概念和之前一样)
class MyMeta(type):
def __new__(cls, name, bases, namespace):
type_hints = namespace["__annotations__"]
default_values = {k: namespace[k] for k in type_hints if k in namespace}
print(type_hints)
print(default_values)
class MyClass(metaclass=MyMeta):
x: int
y: str | None
z: float = 0.1
输出:
{'x': 'int', 'y': 'str | None', 'z': 'float'}
{'z': 0.1}
现在您可以检查实际的源代码并跟踪它们的实现。
inspect
模块中有许多有用的功能,可让您在运行时检查对象的许多信息。